[Bug tree-optimization/104475] [12/13 Regression] Wstringop-overflow + atomics incorrect warning on dynamic object
thiago at kde dot org
gcc-bugzilla@gcc.gnu.org
Tue Dec 6 18:03:33 GMT 2022
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104475
--- Comment #19 from Thiago Macieira <thiago at kde dot org> ---
(In reply to Richard Biener from comment #15)
> Thanks, it's still the same reason - we isolate a nullptr case and end up
> with
>
> __atomic_or_fetch_4 (184B, 64, 0); [tail call]
>
> The path we isolate is d->m_mutex == nullptr && !enable in
>
> void QFutureInterfaceBase::setThrottled(bool enable)
> {
> QMutexLocker lock(&d->m_mutex);
Thank you for the analysis, Richard. But do note that it's &d->m_mutex, not
d->m_mutex that is passed to the locker. C++ says that if you do d-> then d !=
nullptr, so &d->m_mutex can't be nullptr either.
However, I guess GCC thinks it can be because the offset of m_mutex in QFIBP is
zero. pahole says:
public:
void QFutureInterfaceBasePrivate(class QFutureInterfaceBasePrivate *,
enum State);
void ~QFutureInterfaceBasePrivate(class QFutureInterfaceBasePrivate *,
int);
class QMutex m_mutex; /* 0 8 */
class QBasicMutex continuationMutex; /* 8 8 */
So there's a missed optimisation here. But it doesn't look like GCC is the only
one to miss it, see https://gcc.godbolt.org/z/WW5hbW6sW. Maybe it's an
intentional choice?
> we predict the path to be unlikely but the adjustment to the threader
> covered probably never executed paths (with probability zero). The
> threading opportunity arises because the DTOR calls
>
> inline void unlock() noexcept
> {
> if (!isLocked)
> return;
> m->unlock();
> isLocked = false;
> }
>
> and we know isLocked on the nullptr path.
We know it can't be true.
> I thought we could maybe enhance prediction to look for nullptr based
> accesses but at the time we estimate probabilities the QMutexLocker
> CTOR isn't yet inlined (the DTOR is partially inlined, exposing the
> isLocked check).
>
> Note the "impossible" path is actually in the sources - so there might
> be a missing conditional somewhere.
I don't see it, but that's probably because I'm looking at it from the C++
side. If the mutex pointer that was passed is null, then isLocked is never set
to true. What you're saying is that the unlock() function above was inlined and
that GCC knew m to be nullptr, but didn't know isLocked's value... which makes
no sense to me. If the constructor wasn't inlined, it couldn't know the value
of m either. If the constructor was inlined, then it should know the value of
both.
Anyway, this discussion made me realise there's a series of changes to
QMutexLocker ending in "QMutexLocker: strenghten the locking operations"
(https://code.qt.io/cgit/qt/qtbase.git/commit/?id=1b1456975347b044c11169458b53c9f6083dbc59).
This probably did change how the optimiser works, explaining why the warnings
went away.
But it shouldn't have. We went from
inline ~QMutexLocker() {
unlock();
}
inline void unlock() noexcept
{
if (!isLocked)
return;
m->unlock();
isLocked = false;
}
to
inline ~QMutexLocker()
{
if (m_isLocked)
unlock();
}
inline void unlock() noexcept
{
Q_ASSERT(m_isLocked);
m_mutex->unlock();
m_isLocked = false;
}
with the Q_ASSERT expanding to nothing in release builds, it should be
effectively identical code.
More information about the Gcc-bugs
mailing list