This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Fix devirtualiation in expanded thunks


Hi Jan

On 21/12/18 7:20 PM, Jan Hubicka wrote:
> Hi,
> this patch fixes polymorphic call analysis in thunks.  Unlike normal
> methods, thunks take THIS pointer offsetted by a known constant. This
> needs t be compensated for when calculating address of outer type.
>
> Bootstrapped/regtested x86_64-linux, also tested with Firefox where this
> bug trigger misoptimization in spellchecker.  I plan to backport it to
> release branches soon.
>
> Honza
>
> 	PR ipa/88561
> 	* ipa-polymorphic-call.c
> 	(ipa_polymorphic_call_context::ipa_polymorphic_call_context): Handle
> 	arguments of thunks correctly.
> 	(ipa_polymorphic_call_context::get_dynamic_context): Be ready for
> 	NULL instance pinter.
> 	* lto-cgraph.c (lto_output_node): Always stream thunk info.
> 	* g++.dg/tree-prof/devirt.C: New testcase.
> Index: ipa-polymorphic-call.c
> ===================================================================
> --- ipa-polymorphic-call.c	(revision 267325)
> +++ ipa-polymorphic-call.c	(working copy)
> @@ -995,9 +995,22 @@ ipa_polymorphic_call_context::ipa_polymo
>   	{
>   	  outer_type
>   	     = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (base_pointer)));
> +	  cgraph_node *node = cgraph_node::get (current_function_decl);
>   	  gcc_assert (TREE_CODE (outer_type) == RECORD_TYPE
>   		      || TREE_CODE (outer_type) == UNION_TYPE);
>   
> +	  /* Handle the case we inlined into a thunk.  In this case
> +	     thunk has THIS pointer of type bar, but it really receives
> +	     address to its base type foo which sits in bar at
> +	     0-thunk.fixed_offset.  It starts with code that adds
> +	     think.fixed_offset to the pointer to compensate for this.
> +
> +	     Because we walked all the way to the begining of thunk, we now
> +	     see pointer &bar-thunk.fixed_offset and need to compensate
> +	     for it.  */
> +	  if (node->thunk.fixed_offset)
> +	    offset -= node->thunk.fixed_offset * BITS_PER_UNIT;
> +
>   	  /* Dynamic casting has possibly upcasted the type
>   	     in the hiearchy.  In this case outer type is less
>   	     informative than inner type and we should forget
> @@ -1005,7 +1018,11 @@ ipa_polymorphic_call_context::ipa_polymo
>   	  if ((otr_type
>   	       && !contains_type_p (outer_type, offset,
>   				    otr_type))
> -	      || !contains_polymorphic_type_p (outer_type))
> +	      || !contains_polymorphic_type_p (outer_type)
> +	      /* If we compile thunk with virtual offset, the THIS pointer
> +		 is adjusted by unknown value.  We can't thus use outer info
> +		 at all.  */
> +	      || node->thunk.virtual_offset_p)
>   	    {
>   	      outer_type = NULL;
>   	      if (instance)
> @@ -1030,7 +1047,15 @@ ipa_polymorphic_call_context::ipa_polymo
>   	      maybe_in_construction = false;
>   	    }
>   	  if (instance)
> -	    *instance = base_pointer;
> +	    {
> +	      /* If method is expanded thunk, we need to apply thunk offset
> +		 to instance pointer.  */
> +	      if (node->thunk.virtual_offset_p
> +		  || node->thunk.fixed_offset)
> +		*instance = NULL;
> +	      else
> +	        *instance = base_pointer;
> +	    }
>   	  return;
>   	}
>         /* Non-PODs passed by value are really passed by invisible
> @@ -1547,6 +1572,9 @@ ipa_polymorphic_call_context::get_dynami
>     HOST_WIDE_INT instance_offset = offset;
>     tree instance_outer_type = outer_type;
>   
> +  if (!instance)
> +    return false;
> +
>     if (otr_type)
>       otr_type = TYPE_MAIN_VARIANT (otr_type);
>   
> Index: lto-cgraph.c
> ===================================================================
> --- lto-cgraph.c	(revision 267325)
> +++ lto-cgraph.c	(working copy)
> @@ -547,7 +547,11 @@ lto_output_node (struct lto_simple_outpu
>     streamer_write_bitpack (&bp);
>     streamer_write_data_stream (ob->main_stream, section, strlen (section) + 1);
>   
> -  if (node->thunk.thunk_p)
> +  /* Stream thunk info always because we use it in
> +     ipa_polymorphic_call_context::ipa_polymorphic_call_context
> +     to properly interpret THIS pointers for thunks that has been converted
> +     to Gimple.  */
> +  if (node->definition)
>       {
>         streamer_write_uhwi_stream
>   	 (ob->main_stream,
> @@ -1295,7 +1299,7 @@ input_node (struct lto_file_decl_data *f
>     if (section)
>       node->set_section_for_node (section);
>   
> -  if (node->thunk.thunk_p)
> +  if (node->definition)
>       {
>         int type = streamer_read_uhwi (ib);
>         HOST_WIDE_INT fixed_offset = streamer_read_uhwi (ib);
> Index: testsuite/g++.dg/tree-prof/devirt.C
> ===================================================================
> --- testsuite/g++.dg/tree-prof/devirt.C	(nonexistent)
> +++ testsuite/g++.dg/tree-prof/devirt.C	(working copy)
> @@ -0,0 +1,123 @@
> +/* { dg-options "-O3 -fdump-tree-dom3" } */
> +struct nsISupports
> +{
> +  virtual int QueryInterface (const int &aIID, void **aInstancePtr) = 0;
> +  virtual __attribute__((noinline, noclone)) unsigned AddRef (void) = 0;
> +  virtual unsigned Release (void) = 0;
> +};
> +
> +struct nsIObserver : public nsISupports
> +{
> +  virtual int Observe (nsISupports * aSubject, const char *aTopic, const unsigned short *aData) = 0;
> +};
> +
> +struct nsISupportsWeakReference : public nsISupports
> +{
> +  virtual int GetWeakReference (void **_retval) = 0;
> +};
> +
> +struct nsSupportsWeakReference : public nsISupportsWeakReference
> +{
> +  nsSupportsWeakReference () : mProxy (0) {}
> +  virtual int GetWeakReference (void **_retval) override { return 0; }
> +  ~nsSupportsWeakReference () {}
> +  void NoticeProxyDestruction () { mProxy = nullptr; }
> +  void *mProxy;
> +  void ClearWeakReferences ();
> +  bool HasWeakReferences () const { return !!mProxy; }
> +};
> +
> +struct mozIPersonalDictionary : public nsISupports
> +{
> +  virtual int Load (void) = 0;
> +  virtual int Save (void) = 0;
> +  virtual int GetWordList (void **aWordList) = 0;
> +  virtual int Check (const int &word, bool * _retval) = 0;
> +  virtual int AddWord (const int &word) = 0;
> +  virtual int RemoveWord (const int &word) = 0;
> +  virtual int IgnoreWord (const int &word) = 0;
> +  virtual int EndSession (void) = 0;
> +};
> +
> +struct mozPersonalDictionary final
> +  : public mozIPersonalDictionary, public nsIObserver, public nsSupportsWeakReference
> +{
> +  virtual int QueryInterface (const int &aIID, void **aInstancePtr) override;
> +  virtual __attribute__((noinline, noclone)) unsigned AddRef (void) override;
> +  virtual unsigned Release (void) override;
> +  unsigned long mRefCnt;
> +  virtual int Load (void) override { return 0; }
> +  virtual int Save (void) override { return 0; }
> +  virtual int GetWordList (void **aWordList) override { return 0; }
> +  virtual int Check (const int &word, bool * _retval) override { return 0; }
> +  virtual int AddWord (const int &word) override { return 0; }
> +  virtual int RemoveWord (const int &word) override { return 0; }
> +  virtual int IgnoreWord (const int &word) override { return 0; }
> +  virtual int EndSession (void) override { return 0; }
> +  virtual int Observe (nsISupports * aSubject, const char *aTopic, const unsigned short *aData) override { return 0; }
> +  mozPersonalDictionary () : mRefCnt(0) {}
> +  int Init () { return 0; }
> +  virtual ~mozPersonalDictionary () {}
> +  bool mIsLoaded;
> +  bool mSavePending;
> +  void *mFile;
> +  char mMonitor[96];
> +  char mMonitorSave[96];
> +  char mDictionaryTable[32];
> +  char mIgnoreTable[32];
> +};
> +
> +unsigned
> +mozPersonalDictionary::AddRef (void)
> +{
> +  unsigned count = ++mRefCnt;
> +  return count;
> +}
> +
> +unsigned
> +mozPersonalDictionary::Release (void)
> +{
> +  unsigned count = --mRefCnt;
> +  if (count == 0)
> +    {
> +      mRefCnt = 1;
> +      delete (this);
> +      return 0;
> +    }
> +  return count;
> +}
> +
> +int
> +mozPersonalDictionary::QueryInterface (const int &aIID, void **aInstancePtr)
> +{
> +  nsISupports *foundInterface;
> +  if (aIID == 122)
> +    foundInterface = static_cast <mozIPersonalDictionary *>(this);
> +  else
> +    foundInterface = static_cast <nsISupportsWeakReference *>(this);
> +  int status;
> +  foundInterface->AddRef ();
> +  *aInstancePtr = foundInterface;
> +  return status;
> +}
> +
> +__attribute__((noipa)) int
> +foo (nsISupports *p, const int &i)
> +{
> +  void *q;
> +  return p->QueryInterface (i, &q);
> +}
> +
> +int
> +main ()
> +{
> +  mozPersonalDictionary m;
> +  int j = 123;
> +  for (int i = 0; i < 100000; i++)
> +    foo (static_cast <nsISupportsWeakReference *>(&m), j);
> +  if (m.mRefCnt != 100000)
> +    __builtin_abort ();
> +}
> +
This patch causes failures on aarch64-none-linux-gnu and 
arm-none-linux-gnueabihf

UNRESOLVED: g++.dg/tree-prof/devirt.C scan-ipa-dump-times dom3 "3" 
folding virtual function call to virtual unsigned int 
mozPersonalDictionary::AddRef
UNRESOLVED: g++.dg/tree-prof/devirt.C scan-ipa-dump-times dom3 "3" 
folding virtual function call to virtual unsigned int 
mozPersonalDictionary::_ZThn16

With

g++.dg/tree-prof/devirt.C: dump file does not exist

I had missed this one earlier thinking its the same with the LTO test 
failures and only realized this after the backport commit yesterday by 
Martin.

Thanks

Sudi

> +/* { dg-final-use-not-autofdo { scan-ipa-dump-times 3 "folding virtual function call to virtual unsigned int mozPersonalDictionary::_ZThn16" "dom3" } } */
> +/* { dg-final-use-not-autofdo { scan-ipa-dump-times 3 "folding virtual function call to virtual unsigned int mozPersonalDictionary::AddRef" "dom3" } } */

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]