Home > front end >  Why does MSVC compiler put template instantiation binaries in assembly?
Why does MSVC compiler put template instantiation binaries in assembly?

Time:04-03

I encountered something weird in the MSVC compiler.

it puts function template definition in assembly while optimization eliminates the need for them. It seems that Clang and GCC successfully remove function definition at all but MSVC does not.

Can it be fixed?

main.cpp:

#include <iostream>

template <int n> int value() noexcept
{
    return n;
}

int main()
{
    return value<5>()   value<10>();
}

assembly:

int value<5>(void) PROC                                ; value<5>, COMDAT
        mov     eax, 5
        ret     0
int value<5>(void) ENDP                                ; value<5>

int value<10>(void) PROC                                ; value<10>, COMDAT
        mov     eax, 10
        ret     0
int value<10>(void) ENDP                                ; value<10>

main    PROC                                            ; COMDAT
        mov     eax, 15
        ret     0
main    ENDP

Sample code on godbolt

CodePudding user response:

The /FA switch generates the listing file for each translation unit. Since this is before the linking stage, MSVC does not determine if those two functions are required anywhere else within the program, and are thus still included in the generated .asm file (Note: this may be for simplicity on MS's part, since it can treat templates the same as regular functions in the generated .obj file, though realistically there's no actual need to store them in the .obj file, as user17732522 points out in the comments).

During linking, MSVC determines that those functions are in fact not actually used / needed anywhere else, and thus can be eliminated (even if they were used elsewhere, since the result can be determined at compile time, they'd still be eliminated) from the compiled executable.

In order to see what's in the final compiled executable, you can view the executable through a disassembler. Example for using MSVC to do this, is put a breakpoint in the main function, run it, then when the breakpoint is hit, right click and "View Disassembly". In this, you will see that the two functions don't exist anymore.

You can also generate the Mapfile using /MAP option, which also shows it does not exist.

CodePudding user response:

Just add /Zc:inline to your compile statement and it does the same thing as clang/GCC if you also wrap the template in an anonymous namespace to ensure it does not have external visibility.

#include <iostream>

namespace
{
    template <int n> int value() noexcept
    {
        return n;
    }
}

or if you mark the template function inline

template <int n> inline int value() noexcept
{
    return n;
}

Both result in:

main    PROC
        mov     eax, 15
        ret     0
main    ENDP

The /Zc:inline (Remove unreferenced COMDAT) switch was added in VS 2015 Update 2 as part of the C 11 Standard conformance which allows this optimization.

It is off-by-default in command-line builds. In MSBuild, <RemoveUnreferencedCodeData> defaults to true.

See Microsoft Docs

OTHERWISE It will be cleaned up in the linker phase with /OPT:REF.

  • Related