Basic kill analysis for modref

Jan Hubicka hubicka@kam.mff.cuni.cz
Fri Nov 12 10:47:58 GMT 2021


> 
> I wonder why we bother producing summaries for things that do not
> bind locally?  The summary->kills.length () has an upper bound?

Because of local aliases.
The size of the array is capped by param_max_modref_accesses which is
16.
> 
> > +	  && summary->kills.length ())
> > +	{
> > +	  tree base = ao_ref_base (ref);
> > +	  for (unsigned int i = 0; i < summary->kills.length (); i++)
> > +	    {
> > +	      modref_access_node &a = summary->kills[i];
> > +	      tree op;
> > +	      poly_offset_int off1_adj = 0, off2_adj = 0;
> > +	      poly_int64 off1, off2;
> > +	      tree base_ptr = NULL;
> > +	      tree base_decl = NULL;
> > +
> > +	      if (a.parm_index >= 0)
> > +		op = gimple_call_arg (stmt, a.parm_index);
> > +	      else if (a.parm_index == MODREF_STATIC_CHAIN_PARM)
> > +		op = gimple_call_chain (stmt);
> > +	      else
> > +		gcc_unreachable ();
> 
> I wonder if we can abstract this to a modref_access_node method?

Something like get_param (stmt)? Yes, it looks like a good idea.
> 
> > +
> > +	      off2_adj += a.parm_offset * BITS_PER_UNIT;
> 
> wasn't there a parm_offset unknown? ...
Yes, but we do not insert those accesses to kills since they are
unknown.
> 
> > +	      if (!(off2_adj + a.offset).to_shwi (&off2))
> > +		continue;
> > +	      if (TREE_CODE (base) == MEM_REF)
> > +		{
> > +		  off1_adj = mem_ref_offset (base) << LOG2_BITS_PER_UNIT;
> > +		  if (TREE_CODE (TREE_OPERAND (base, 0)) == ADDR_EXPR)
> > +		    base_decl = TREE_OPERAND (TREE_OPERAND (base, 0), 0);
> 
> 'base' will be the decl in this case, apart from when the constant
> offset doesn't fit ao_ref.offset, so I think you can spare this
> special-case and give up on non-SSA base_ptr

I tought we wrap decls to modrefs in lto streaming when type merging
fails?
> 
> > +		  else
> > +		    base_ptr = TREE_OPERAND (base, 0);
> > +		}
> > +	      /* Give up on TMRs for now.  */
> > +	      else if (TREE_CODE (base) == TARGET_MEM_REF)
> > +		break;
> > +	      else
> > +		base_decl = base;
> > +
> > +	      gcc_checking_assert (!base_decl || DECL_P (base_decl));
> > +	      gcc_checking_assert (!base_ptr
> > +				   || TREE_CODE (base_ptr) == SSA_NAME);
> > +
> > +	      /* OP is a pointer and we have access range from its
> > +		 dereference.  */
> > +	      if (TREE_CODE (op) == ADDR_EXPR)
> > +		{
> > +		  poly_int64 size, offset, max_size;
> > +		  bool reverse;
> > +		  tree op_base = get_ref_base_and_extent
> > +			  (TREE_OPERAND (op, 0), &offset, &size,
> > +			   &max_size, &reverse);
> 
> I think you want get_addr_base_and_unit_offset here.  All
> variable indexed addresses are in separate stmts.  That also means
> you can eventually work with just byte sizes/offsets?

Will do.  The access range in modref summary is bit based (since we want
to disabiguate bitfields like we do in rest of alias oracle) but indeed
this part cna be in bytes.
> 
> > +		  if (!known_size_p (size) || !known_eq (size, max_size))
> > +		    continue;
> > +		  off2_adj += offset;
> > +		  /* &str->foo are not passed as gimple operands directly,
> > +		     would need to look up the def stmt.  */
> > +		  gcc_checking_assert (TREE_CODE (op_base) != MEM_REF);
> > +		  if (!base_decl
> > +		      || compare_base_decls (op_base, base_decl) != 1)
> > +		    continue;
> > +		}
> > +	      else if (!base_ptr || !operand_equal_p (base_ptr, op))
> > +		continue;
> > +
> > +	      if (!(off1_adj + ref->offset).to_shwi (&off1))
> > +		continue;
> > +	      if (!(off2_adj + a.offset).to_shwi (&off2))
> > +		continue;
> > +
> > +	      if (known_subrange_p (off1, ref->max_size, off2, a.size)
> > +		  && dbg_cnt (ipa_mod_ref))
> > +		{
> > +		  /* For store to be killed it needs to not be used earlier.  */
> > +		  if (ref_maybe_used_by_call_p_1 (as_a <gcall *> (stmt), ref,
> > +						  true))
> 
> Hmm, so moderf says p->x is killed when we have
> 
> foo (struct X *p)
> {
>   int tem = p->x;
>   p->x = 0;
>   return tem;
> }
> 
> ?  Or even
Yep, this will currently land in kills.  I can add loop pruning kills
with known load ranges incrementally.
> 
> foo (struct X *p)
> {
>   bar ();
>   p->x = 0;
> }
> 
> ?
Here we will end up with reading global memory and that will turn kills
empty in modref.

The check is still needed to verify that ref is not passed as aggregate
parameter.

I will update patch.
Thanks,
Honza
> 
> Otherwise it looks sensible.
> 
> Richard.


More information about the Gcc-patches mailing list