https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88561
Bug ID: 88561 Summary: [8/9 Regression] PGO devirtualization miscompilation of firefox Product: gcc Version: 9.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: ipa Assignee: unassigned at gcc dot gnu.org Reporter: jakub at gcc dot gnu.org CC: marxin at gcc dot gnu.org Target Milestone: --- g++ -O3 -fprofile-generate -o test{1,.C}; ./test1; g++ -O3 -fprofile-use -o test{2,.C}; ./test2 aborts when using g++ 8 or current trunk. 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 (); } Testcase reduced from firefox https://bugzilla.mozilla.org/show_bug.cgi?id=1495742 The problem is that in the _ZThn8_N21mozPersonalDictionary14QueryInterfaceERKiPPv thunk we devirtualize incorrectly, call _ZThn8_N21mozPersonalDictionary6AddRefEv when we should have called _ZThn16_N21mozPersonalDictionary6AddRefEv instead.