Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::mutex::lock() produces weird (and unnecessary) asm code

Tags:

c++

gcc

c++11

I was checking generated asm for some of my code and my eye caught some interesting stuff:

#include <mutex>

std::mutex m;

void foo()
{
    m.lock();
}

generated asm code (x86-64 gcc 9.2, -std=c++11 -O2):

foo():
        mov     eax, OFFSET FLAT:_ZL28__gthrw___pthread_key_createPjPFvPvE
        test    rax, rax
        je      .L10                  // (1) we can simply bypass lock() call?
        sub     rsp, 8
        mov     edi, OFFSET FLAT:m
        call    __gthrw_pthread_mutex_lock(pthread_mutex_t*)
        test    eax, eax
        jne     .L14                  // (2) waste of space that will never be executed
        add     rsp, 8
        ret
.L10:
        ret
.L14:
        mov     edi, eax
        call    std::__throw_system_error(int)
m:
        .zero   40

Questions:

  • part (1) -- gcc specific:
    • what it is doing? (allocating TLS entry?)
    • how failing that operation allows us to silently bypass lock() call?
  • part (2) -- looks like each compiler is affected:
    • std::mutex::lock() can throw according to standard
    • ... but it never does in correct code (as discussed in related SO posts), for all intents and purposes std::mutex::lock() is always noexcept in correct code
    • is it possible to let compiler know so that it stops emitting unnecessary tests and instruction blocks (like .L14 above)?

Note: I can't see how throwing from std::mutex::lock() is better than simply abort()ing. In both cases your program is screwed (no one expects it to fail), but at least in latter case you end up with considerably smaller asm code ("pay only for something you use", remember?).

like image 746
C.M. Avatar asked Dec 04 '19 19:12

C.M.


1 Answers

It seems that you are misinterpreting the asm output. What you see is not the code of foo but the inlined code of mutex::lock.

From https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/std/mutex:

void lock() // in class mutex
{
    int __e = __gthread_recursive_mutex_lock(&_M_mutex);

    // EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
    if (__e)
        __throw_system_error(__e);
}

From https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.0/gthr-default_8h-source.html:

static inline int __gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *mutex)
{
    return __gthread_mutex_lock (mutex);
}

static inline int __gthread_mutex_lock (__gthread_mutex_t *mutex)
{
    if (__gthread_active_p ())
        return pthread_mutex_lock (mutex);
    else
        return 0;
}

The names do not exactly match your asm code, so I probably looked at a different libstdc++ source, but to me it looks like the compiler inlined mutex::lock into your function foo and it also inlined the functions that mutex::lock is calling.

like image 82
Werner Henze Avatar answered Nov 14 '22 23:11

Werner Henze