Chris_F
Chris_F

Reputation: 5557

Why does printf generate an extra function when using MinGW-w64?

Looking at the assembly output by GCC with -O2 I see that if I use printf GCC will create a function named, for example, _Z6printfPKcz, which then calls __mingw_vprintf. What is the purpose for this indirection? Why doesn't printf translate directly to a call to __mingw_printf? This is the same for the other related functions, such as sprintf.

Edit:

For the record, I know what name mangling is and how it works. My question is why is the compiler generating the function in the first place, a function which does nothing but forward the arguments to __mingw_vprintf, when it could simply call __mingw_printf directly and save the unnecessary indirection.

In other words, why should this:

printf("%d\n", var);

compile to this:

_Z6printfPKcz:
    sub rsp, 56
    mov QWORD PTR 72[rsp], rdx
    lea rdx, 72[rsp]
    mov QWORD PTR 80[rsp], r8
    mov QWORD PTR 88[rsp], r9
    mov QWORD PTR 40[rsp], rdx
    call    __mingw_vprintf
    add rsp, 56
    ret

main:
    ...
    call    _Z6printfPKcz
    ...

when this would suffice

main:
    ...
    call    __mingw_printf
    ...

Upvotes: 4

Views: 1332

Answers (4)

Ali
Ali

Reputation: 58451

There are three things to be realized here and none of the answers contains all three of them (at the moment at least).

  1. _Z6printfPKcz is the mangled name of printf. See immibis' answer.

  2. There are several variants of the printf functions and one or only a few functions implementing the functionality. The latter function(s), whose name start with __, do the heavy lifting and are internal to the implementation; the functions exposed to the user don't do any significant work, they basically just forward the call either to another forwarding function or to the implementing function. See Mats Petersson's answer.

  3. Inlining of the forwarding function failed in your case. See Basilevs' answer.

I suspect, based on your comments, that your real question is why the inlining failed, why the implementation isn't called directly, without any overhead. Good question, I don't see any valid technical reason for that.

I know that gcc (at least on Linux) has the tendency not to inline functions if link-time optimization is not used, even if both the function body (implementation) and the call site is in the same translation unit (in the same source file).

Try to enable link-time optimization. See -flto among the Options That Control Optimization. It is very likely that the call will be inlined with link time optimization; that functions seems to be a good candidate for inlining.


UPDATE: I cannot test mingw; here are my tests on Linux. This code:

#include <stdio.h>

int main(int argc, char** argv) {
  printf("%d\n", argc);
}

results in the following assembly with optimizations disabled -O0:

    [...]
    call    printf
    [...] 

That is, the call to printf is not inlined. However, already at -O1 I get:

    [...]
    call    __printf_chk
    [...]

which means that the call to printf was inlined and I am directly calling the underlying implementation. It didn't even require link time optimization. You only need to motivate the compiler to do the inlining.

Giving it some thought: The compiler may not inline functions at compile time that are in the C runtime because they are supposed to be accessed at runtime. But it definitely inlines the wrapper function already at -O1.

As to why a particular version of a compiler on a particular platform fails to inline a particular function, well... If you think it is a performance hit in your application, try to submit a bug report. If I were you, I wouldn't worry to much about it, the IO will cost you a lot more time than that extra function call due to the failed inline.

Upvotes: 1

Mats Petersson
Mats Petersson

Reputation: 129364

_pZ6printfPKcz is a C++ mangled name for a function that has the signature printf(char const*, ...) - in other words, the actual printf function. Which, it would appear is implemented by calling another function. Since there are half a dozen different variants of printf (with different types of output, such as output to a file, output to a string, output to console, etc), it makes sense to have a single, "do printf" function - which in this case appears to be __mingq_vprintf. Since a printf implementation is a few thousand lines of code, we don't really want to duplicate it 6 or so times for all the different variants (I know, because at work I've been implementing a version of printf for OpenCL - it's a pretty complicated function, you DEFINITELY don't want to have the actual code several times over).

You can see the exact definition of printf in the "stdio.h" here:

http://sourceforge.net/apps/trac/mingw-w64/browser/trunk/mingw-w64-headers/crt/stdio.h?rev=5437#L283

As to exactly why they do this, it's really something you have to ask the developers of mingw - but my assumption would be that they are trying to keep the number of different variants down - for example __mingw_vprintf is also used to implement the actual vprintf variant, so that's "half as many variants already".

The extra layering also allows the printf to be switched between narrow (ASCII) and wide characters (so called UNICODE), as can be seen by the macros here:

http://sourceforge.net/apps/trac/mingw-w64/browser/trunk/mingw-w64-crt/stdio/mingw_pformat.h?rev=3972#L69

And you'll hate the mingw developers even more when you realize that __mingw_vprintf turns into just another layer of call:

http://sourceforge.net/apps/trac/mingw-w64/browser/trunk/mingw-w64-crt/stdio/mingw_vprintf.c?rev=2296#L51

However, in the whole scheme of a printf implementation, like I said in my comment, it's very small amounts of overhead - the _pformat code is a good 2000 lines long:

http://sourceforge.net/p/mingw/mingw-org-wsl/ci/21762bb4a1bd0c88c38eead03f59e8d994349e83/tree/misc/src/libcrt/stdio/pformat.c

(Which is much smaller than the glibc implementation)

Upvotes: 2

Basilevs
Basilevs

Reputation: 23916

Printf fails to be inlined in this specific case. If inline fails, compiler have to create an explicit function body. Usual behavior without indirection is explained by successful inline.

Upvotes: 1

_Z6printfPKcz is just the mangled name of printf - search "name mangling" for more information on that. It's the actual printf function.

__mingw_vprintf is an internal function which printf calls - there's no requirement that printf doesn't call another function to do its work. Maybe __mingw_vprintf handles all the formatting and printing, and printf is written like this:

int printf(const char *fmt, ...)
{
    va_list va;
    va_start(va, fmt);
    int result = __mingw_vprintf(fmt, va);
    va_end(va);
    return result;
}

Upvotes: 5

Related Questions