On Mon, 2 Mar 2020, Jakub Jelinek wrote: > On Mon, Mar 02, 2020 at 12:46:30PM +0100, Richard Biener wrote: > > > + void *r = data.push_partial_def (pd, 0, prec); > > > + if (r == (void *) -1) > > > + return NULL_TREE; > > > + gcc_assert (r == NULL_TREE); > > > + } > > > + pos += tz; > > > + if (pos == prec) > > > + break; > > > + w = wi::lrshift (w, tz); > > > + tz = wi::ctz (wi::bit_not (w)); > > > + if (pos + tz > prec) > > > + tz = prec - pos; > > > + pos += tz; > > > + w = wi::lrshift (w, tz); > > > + } > > > > I'd do this in the vn_walk_cb_data CTOR instead - you pass mask != > > NULL_TREE anyway so you can as well pass mask. > > I've tried, but have no idea how to handle the case where > data.push_partial_def (pd, 0, prec); fails above if it is done in the > constructor. > Though, the BIT_AND_EXPR case already checks: > + && CHAR_BIT == 8 > + && BITS_PER_UNIT == 8 > + && BYTES_BIG_ENDIAN == WORDS_BIG_ENDIAN > and also checks the pathological cases of mask being all ones or all zeros, > so it is just the theoretical case of > maxsizei > bufsize * BITS_PER_UNIT > so maybe it is moot and we can just assert that push_partial_def > returned NULL. > > > I wonder if we can instead make the above return NULL (finish > > return (void *)-1) and do sth like > > > > if (!wvnresult && mask) > > return data.masked_result; > > > > and thus avoid the type-"unsafe" return frobbing by storing the > > result value in an extra member of the vn_walk_cb_data struct. > > Done that way. > > > Any reason you piggy-back on visit_reference_op_load instead of using > > vn_reference_lookup directly? I'd very much prefer that since it > > doesn't even try to mess with the SSA lattice. > > I didn't want to duplicate the VCE case, but it isn't that long.
Ah, I'm not sure this will ever trigger though, does it? > So, like this if it passes bootstrap/regtest? Yes, this is OK (remove the VCE case at your discretion). Thanks, Richard. > 2020-03-02 Jakub Jelinek <ja...@redhat.com> > > PR tree-optimization/93582 > * tree-ssa-sccvn.h (vn_reference_lookup): Add mask argument. > * tree-ssa-sccvn.c (struct vn_walk_cb_data): Add mask and masked_result > members, initialize them in the constructor and if mask is non-NULL, > artificially push_partial_def {} for the portions of the mask that > contain zeros. > (vn_walk_cb_data::finish): If mask is non-NULL, set masked_result to > val and return (void *)-1. Formatting fix. > (vn_reference_lookup_pieces): Adjust vn_walk_cb_data initialization. > Formatting fix. > (vn_reference_lookup): Add mask argument. If non-NULL, don't call > fully_constant_vn_reference_p nor vn_reference_lookup_1 and return > data.mask_result. > (visit_nary_op): Handle BIT_AND_EXPR of a memory load and INTEGER_CST > mask. > (visit_stmt): Formatting fix. > > * gcc.dg/tree-ssa/pr93582-10.c: New test. > * gcc.dg/pr93582.c: New test. > * gcc.c-torture/execute/pr93582.c: New test. > > --- gcc/tree-ssa-sccvn.h.jj 2020-02-28 17:32:56.391363613 +0100 > +++ gcc/tree-ssa-sccvn.h 2020-03-02 13:52:17.488680037 +0100 > @@ -256,7 +256,7 @@ tree vn_reference_lookup_pieces (tree, a > vec<vn_reference_op_s> , > vn_reference_t *, vn_lookup_kind); > tree vn_reference_lookup (tree, tree, vn_lookup_kind, vn_reference_t *, bool, > - tree * = NULL); > + tree * = NULL, tree = NULL_TREE); > void vn_reference_lookup_call (gcall *, vn_reference_t *, vn_reference_t); > vn_reference_t vn_reference_insert_pieces (tree, alias_set_type, tree, > vec<vn_reference_op_s> , > --- gcc/tree-ssa-sccvn.c.jj 2020-02-28 17:32:56.390363628 +0100 > +++ gcc/tree-ssa-sccvn.c 2020-03-02 15:48:12.982620557 +0100 > @@ -1686,15 +1686,55 @@ struct pd_data > struct vn_walk_cb_data > { > vn_walk_cb_data (vn_reference_t vr_, tree orig_ref_, tree *last_vuse_ptr_, > - vn_lookup_kind vn_walk_kind_, bool tbaa_p_) > + vn_lookup_kind vn_walk_kind_, bool tbaa_p_, tree mask_) > : vr (vr_), last_vuse_ptr (last_vuse_ptr_), last_vuse (NULL_TREE), > - vn_walk_kind (vn_walk_kind_), tbaa_p (tbaa_p_), > - saved_operands (vNULL), first_set (-2), known_ranges (NULL) > - { > - if (!last_vuse_ptr) > - last_vuse_ptr = &last_vuse; > - ao_ref_init (&orig_ref, orig_ref_); > - } > + mask (mask_), masked_result (NULL_TREE), vn_walk_kind (vn_walk_kind_), > + tbaa_p (tbaa_p_), saved_operands (vNULL), first_set (-2), > + known_ranges (NULL) > + { > + if (!last_vuse_ptr) > + last_vuse_ptr = &last_vuse; > + ao_ref_init (&orig_ref, orig_ref_); > + if (mask) > + { > + wide_int w = wi::to_wide (mask); > + unsigned int pos = 0, prec = w.get_precision (); > + pd_data pd; > + pd.rhs = build_constructor (NULL_TREE, NULL); > + /* When bitwise and with a constant is done on a memory load, > + we don't really need all the bits to be defined or defined > + to constants, we don't really care what is in the position > + corresponding to 0 bits in the mask. > + So, push the ranges of those 0 bits in the mask as artificial > + zero stores and let the partial def handling code do the > + rest. */ > + while (pos < prec) > + { > + int tz = wi::ctz (w); > + if (pos + tz > prec) > + tz = prec - pos; > + if (tz) > + { > + if (BYTES_BIG_ENDIAN) > + pd.offset = prec - pos - tz; > + else > + pd.offset = pos; > + pd.size = tz; > + void *r = push_partial_def (pd, 0, prec); > + gcc_assert (r == NULL_TREE); > + } > + pos += tz; > + if (pos == prec) > + break; > + w = wi::lrshift (w, tz); > + tz = wi::ctz (wi::bit_not (w)); > + if (pos + tz > prec) > + tz = prec - pos; > + pos += tz; > + w = wi::lrshift (w, tz); > + } > + } > + } > ~vn_walk_cb_data (); > void *finish (alias_set_type, tree); > void *push_partial_def (const pd_data& pd, alias_set_type, HOST_WIDE_INT); > @@ -1703,6 +1743,8 @@ struct vn_walk_cb_data > ao_ref orig_ref; > tree *last_vuse_ptr; > tree last_vuse; > + tree mask; > + tree masked_result; > vn_lookup_kind vn_walk_kind; > bool tbaa_p; > vec<vn_reference_op_s> saved_operands; > @@ -1731,9 +1773,15 @@ vn_walk_cb_data::finish (alias_set_type > { > if (first_set != -2) > set = first_set; > - return vn_reference_lookup_or_insert_for_pieces > - (last_vuse, set, vr->type, > - saved_operands.exists () ? saved_operands : vr->operands, val); > + if (mask) > + { > + masked_result = val; > + return (void *) -1; > + } > + vec<vn_reference_op_s> &operands > + = saved_operands.exists () ? saved_operands : vr->operands; > + return vn_reference_lookup_or_insert_for_pieces (last_vuse, set, vr->type, > + operands, val); > } > > /* pd_range splay-tree helpers. */ > @@ -3380,13 +3428,13 @@ vn_reference_lookup_pieces (tree vuse, a > { > ao_ref r; > unsigned limit = param_sccvn_max_alias_queries_per_access; > - vn_walk_cb_data data (&vr1, NULL_TREE, NULL, kind, true); > + vn_walk_cb_data data (&vr1, NULL_TREE, NULL, kind, true, NULL_TREE); > if (ao_ref_init_from_vn_reference (&r, set, type, vr1.operands)) > - *vnresult = > - (vn_reference_t)walk_non_aliased_vuses (&r, vr1.vuse, true, > - vn_reference_lookup_2, > - vn_reference_lookup_3, > - vuse_valueize, limit, &data); > + *vnresult > + = ((vn_reference_t) > + walk_non_aliased_vuses (&r, vr1.vuse, true, vn_reference_lookup_2, > + vn_reference_lookup_3, vuse_valueize, > + limit, &data)); > gcc_checking_assert (vr1.operands == shared_lookup_references); > } > > @@ -3402,15 +3450,19 @@ vn_reference_lookup_pieces (tree vuse, a > was NULL.. VNRESULT will be filled in with the vn_reference_t > stored in the hashtable if one exists. When TBAA_P is false assume > we are looking up a store and treat it as having alias-set zero. > - *LAST_VUSE_PTR will be updated with the VUSE the value lookup succeeded. > */ > + *LAST_VUSE_PTR will be updated with the VUSE the value lookup succeeded. > + MASK is either NULL_TREE, or can be an INTEGER_CST if the result of the > + load is bitwise anded with MASK and so we are only interested in a subset > + of the bits and can ignore if the other bits are uninitialized or > + not initialized with constants. */ > > tree > vn_reference_lookup (tree op, tree vuse, vn_lookup_kind kind, > - vn_reference_t *vnresult, bool tbaa_p, tree *last_vuse_ptr) > + vn_reference_t *vnresult, bool tbaa_p, > + tree *last_vuse_ptr, tree mask) > { > vec<vn_reference_op_s> operands; > struct vn_reference_s vr1; > - tree cst; > bool valuezied_anything; > > if (vnresult) > @@ -3422,11 +3474,11 @@ vn_reference_lookup (tree op, tree vuse, > vr1.type = TREE_TYPE (op); > vr1.set = get_alias_set (op); > vr1.hashcode = vn_reference_compute_hash (&vr1); > - if ((cst = fully_constant_vn_reference_p (&vr1))) > - return cst; > + if (mask == NULL_TREE) > + if (tree cst = fully_constant_vn_reference_p (&vr1)) > + return cst; > > - if (kind != VN_NOWALK > - && vr1.vuse) > + if (kind != VN_NOWALK && vr1.vuse) > { > vn_reference_t wvnresult; > ao_ref r; > @@ -3438,25 +3490,31 @@ vn_reference_lookup (tree op, tree vuse, > vr1.operands)) > ao_ref_init (&r, op); > vn_walk_cb_data data (&vr1, r.ref ? NULL_TREE : op, > - last_vuse_ptr, kind, tbaa_p); > - wvnresult = > - (vn_reference_t)walk_non_aliased_vuses (&r, vr1.vuse, tbaa_p, > - vn_reference_lookup_2, > - vn_reference_lookup_3, > - vuse_valueize, limit, &data); > + last_vuse_ptr, kind, tbaa_p, mask); > + > + wvnresult > + = ((vn_reference_t) > + walk_non_aliased_vuses (&r, vr1.vuse, tbaa_p, vn_reference_lookup_2, > + vn_reference_lookup_3, vuse_valueize, limit, > + &data)); > gcc_checking_assert (vr1.operands == shared_lookup_references); > if (wvnresult) > { > + gcc_assert (mask == NULL_TREE); > if (vnresult) > *vnresult = wvnresult; > return wvnresult->result; > } > + else if (mask) > + return data.masked_result; > > return NULL_TREE; > } > > if (last_vuse_ptr) > *last_vuse_ptr = vr1.vuse; > + if (mask) > + return NULL_TREE; > return vn_reference_lookup_1 (&vr1, vnresult); > } > > @@ -4664,7 +4722,57 @@ visit_nary_op (tree lhs, gassign *stmt) > } > } > } > - default:; > + break; > + case BIT_AND_EXPR: > + if (INTEGRAL_TYPE_P (type) > + && TREE_CODE (rhs1) == SSA_NAME > + && TREE_CODE (gimple_assign_rhs2 (stmt)) == INTEGER_CST > + && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (rhs1) > + && default_vn_walk_kind != VN_NOWALK > + && CHAR_BIT == 8 > + && BITS_PER_UNIT == 8 > + && BYTES_BIG_ENDIAN == WORDS_BIG_ENDIAN > + && !integer_all_onesp (gimple_assign_rhs2 (stmt)) > + && !integer_zerop (gimple_assign_rhs2 (stmt))) > + { > + gassign *ass = dyn_cast <gassign *> (SSA_NAME_DEF_STMT (rhs1)); > + if (ass > + && !gimple_has_volatile_ops (ass) > + && vn_get_stmt_kind (ass) == VN_REFERENCE) > + { > + tree last_vuse = gimple_vuse (ass); > + tree op = gimple_assign_rhs1 (ass); > + tree result = vn_reference_lookup (op, gimple_vuse (ass), > + default_vn_walk_kind, > + NULL, true, &last_vuse, > + gimple_assign_rhs2 (stmt)); > + if (result) > + { > + /* We handle type-punning through unions by value-numbering > + based on offset and size of the access. Be prepared to > + handle a type-mismatch here via creating a > + VIEW_CONVERT_EXPR. */ > + if (!useless_type_conversion_p (TREE_TYPE (result), > + TREE_TYPE (op))) > + { > + /* We will be setting the value number of lhs to the > + value number of > + VIEW_CONVERT_EXPR <TREE_TYPE (result)> (result). > + So first simplify and lookup this expression to see > + if it is already available. */ > + gimple_match_op res_op (gimple_match_cond::UNCOND, > + VIEW_CONVERT_EXPR, > + TREE_TYPE (op), result); > + result = vn_nary_build_or_lookup (&res_op); > + } > + } > + if (result) > + return set_ssa_val_to (lhs, result); > + } > + } > + break; > + default: > + break; > } > > bool changed = set_ssa_val_to (lhs, lhs); > @@ -5175,14 +5283,14 @@ visit_stmt (gimple *stmt, bool backedges > switch (vn_get_stmt_kind (ass)) > { > case VN_NARY: > - changed = visit_nary_op (lhs, ass); > - break; > + changed = visit_nary_op (lhs, ass); > + break; > case VN_REFERENCE: > - changed = visit_reference_op_load (lhs, rhs1, ass); > - break; > + changed = visit_reference_op_load (lhs, rhs1, ass); > + break; > default: > - changed = defs_to_varying (ass); > - break; > + changed = defs_to_varying (ass); > + break; > } > } > } > --- gcc/testsuite/gcc.dg/tree-ssa/pr93582-10.c.jj 2020-03-02 > 13:52:17.504679803 +0100 > +++ gcc/testsuite/gcc.dg/tree-ssa/pr93582-10.c 2020-03-02 > 13:52:17.504679803 +0100 > @@ -0,0 +1,29 @@ > +/* PR tree-optimization/93582 */ > +/* { dg-do compile { target int32 } } */ > +/* { dg-options "-O2 -fdump-tree-fre1" } */ > +/* { dg-final { scan-tree-dump "return 72876566;" "fre1" { target le } } } */ > +/* { dg-final { scan-tree-dump "return 559957376;" "fre1" { target be } } } > */ > + > +union U { > + struct S { int a : 12, b : 5, c : 10, d : 5; } s; > + unsigned int i; > +}; > +struct A { char a[12]; union U u; }; > +void bar (struct A *); > + > +unsigned > +foo (void) > +{ > + struct A a; > + bar (&a); > + a.u.s.a = 1590; > + a.u.s.c = -404; > +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ > +#define M 0x67e0a5f > +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ > +#define M 0xa5f067e0 > +#else > +#define M 0 > +#endif > + return a.u.i & M; > +} > --- gcc/testsuite/gcc.dg/pr93582.c.jj 2020-03-02 13:52:17.504679803 +0100 > +++ gcc/testsuite/gcc.dg/pr93582.c 2020-03-02 13:52:17.504679803 +0100 > @@ -0,0 +1,57 @@ > +/* PR tree-optimization/93582 */ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -Warray-bounds" } */ > + > +struct S { > + unsigned int s1:1; > + unsigned int s2:1; > + unsigned int s3:1; > + unsigned int s4:1; > + unsigned int s5:4; > + unsigned char s6; > + unsigned short s7; > + unsigned short s8; > +}; > +struct T { > + int t1; > + int t2; > +}; > + > +static inline int > +bar (struct S *x) > +{ > + if (x->s4) > + return ((struct T *)(x + 1))->t1 + ((struct T *)(x + 1))->t2; /* { > dg-bogus "array subscript 1 is outside array bounds of" } */ > + else > + return 0; > +} > + > +int > +foo (int x, int y) > +{ > + struct S s; > /* { dg-bogus "while referencing" } */ > + s.s6 = x; > + s.s7 = y & 0x1FFF; > + s.s4 = 0; > + return bar (&s); > +} > + > +static inline int > +qux (struct S *x) > +{ > + int s4 = x->s4; > + if (s4) > + return ((struct T *)(x + 1))->t1 + ((struct T *)(x + 1))->t2; > + else > + return 0; > +} > + > +int > +baz (int x, int y) > +{ > + struct S s; > + s.s6 = x; > + s.s7 = y & 0x1FFF; > + s.s4 = 0; > + return qux (&s); > +} > --- gcc/testsuite/gcc.c-torture/execute/pr93582.c.jj 2020-03-02 > 13:52:17.504679803 +0100 > +++ gcc/testsuite/gcc.c-torture/execute/pr93582.c 2020-03-02 > 13:52:17.504679803 +0100 > @@ -0,0 +1,22 @@ > +/* PR tree-optimization/93582 */ > + > +short a; > +int b, c; > + > +__attribute__((noipa)) void > +foo (void) > +{ > + b = c; > + a &= 7; > +} > + > +int > +main () > +{ > + c = 27; > + a = 14; > + foo (); > + if (b != 27 || a != 6) > + __builtin_abort (); > + return 0; > +} > > > Jakub > > -- Richard Biener <rguent...@suse.de> SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany; GF: Felix Imendörffer; HRB 36809 (AG Nuernberg)