'Why does MSVC compiler put template instantiation binaries in assembly?
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
Solution 1:[1]
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.
If I am reading the documentation correctly, it seems as those MS chose to include explicit instantiations of templates classes and functions because it "is useful" when creating libraries. Uninstantiated templates are not put into the obj files though.
Solution 2:[2]
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.
Solution 3:[3]
I compiled your code as given on my vs2022 in release mode. I get
return value<5>() + value<10>();
00007FF65CD21000 mov eax,0Fh
}
00007FF65CD21005 ret
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | |
| Solution 3 | pm100 |
