Compilng and linking this file results in a 1-KiB executable:
#pragma comment(linker, "/Entry:mainCRTStartup") // No CRT code (reduce size)
#pragma comment(linker, "/Subsystem:Console") // Needed if avoiding CRT
#define STRINGIFIER(x) func##x
#define STRINGIFY(x) STRINGIFIER(x)
#define G int STRINGIFY(__COUNTER__)(void) { return __COUNTER__; }
int mainCRTStartup(void) { return 0; } // Does nothing
#if 0
// Every `G' generates a new, unused function
G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G
G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G
#endif
When you change #if 0 to #if 1), the output size doubles to 2 KiB.
It seems to do this with all versions of Visual C++ to date, even though my command-line options contain all optimizations I could think of:
/Ox /MD /link /fixed /OPT:ICF /OPT:REF
and, specifically, I did not include any debugging information.
In broad terms… the compiler generates code in “object records” that contains a bunch of assembly code and supporting information. The linker links these object records together to create an executable.
Often a compiler will create a single object record for an entire source file. In this case, the linker can only decide to link in the entire object record, or not. Since there is at least one function in the object record that is used, it must link in all of it.
On some compilers, you can tell it to generate a separate object record for each function (an object file can have multiple object records). In this case, the linker can make the decision to omit some of the object records if they’re never called.
From the Microsoft documentation for /OPT:
The
/Gycompiler option enables function-level linking.For reference, this feature also exists in gcc:
And the companion option in ld: