https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116333
Bug ID: 116333
Summary: unused result of pure function is not optimized out
Product: gcc
Version: 12.2.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: lto
Assignee: unassigned at gcc dot gnu.org
Reporter: pali at kernel dot org
Target Milestone: ---
If some function is marked with __attribute__((pure)) and return value of this
function call is not used at all then gcc could optimize out and completely
drop calling this function.
This kind of optimization does not happen for example below which uses 3 source
files with LTO enabled compilation (so gcc should see inter-file calls).
$ cat pure0.c
extern int main();
__attribute__((used))
int WinMainCRTStartup(void)
{
char * argv[] = { "argv0", (char *)0 };
return main(1, argv);
}
__attribute__((used))
void __main(void) {}
$ cat pure1.c
#define STARTF_USESHOWWINDOW 0x00000001
#define SW_SHOWDEFAULT 10
typedef struct _STARTUPINFOA {
unsigned int cb;
char* lpReserved;
char* lpDesktop;
char* lpTitle;
unsigned int dwX;
unsigned int dwY;
unsigned int dwXSize;
unsigned int dwYSize;
unsigned int dwXCountChars;
unsigned int dwYCountChars;
unsigned int dwFillAttribute;
unsigned int dwFlags;
unsigned short wShowWindow;
unsigned short cbReserved2;
unsigned char* lpReserved2;
void* hStdInput;
void* hStdOutput;
void* hStdError;
} STARTUPINFOA, *LPSTARTUPINFOA;
__declspec(dllimport) extern void __stdcall GetStartupInfoA(LPSTARTUPINFOA);
__attribute__((pure))
static int _winshowcmd (void)
{
STARTUPINFOA StartupInfo = {};
GetStartupInfoA(&StartupInfo);
if (StartupInfo.dwFlags & STARTF_USESHOWWINDOW)
return StartupInfo.wShowWindow;
else
return SW_SHOWDEFAULT;
}
extern int __stdcall WinMain(void *, void *, char *, int);
extern unsigned char __ImageBase[];
int main()
{
return WinMain(&__ImageBase, (void *)0, "argv0", _winshowcmd());
}
$ cat pure2.c
__declspec(dllimport) extern int __stdcall MessageBoxA(void *, const char *,
const char *, unsigned int);
int __stdcall WinMain(void *instance __attribute__((unused)), void
*prev_instance __attribute__((unused)), char *cmdln __attribute__((unused)),
int showcmd __attribute__((unused))) {
MessageBoxA((void *)0, "Message", "Title", 0);
return 0;
}
Two additional helper files are needed to have example self-contained without
any external dependency and also executable on windows:
$ cat kernel32.def
LIBRARY "kernel32.dll"
EXPORTS
GetStartupInfoA@4
$ cat user32.def
LIBRARY "user32.dll"
EXPORTS
MessageBoxA@16
Compile these sources as:
$ i686-w64-mingw32-dlltool -d kernel32.def -k -l libkernel32.a
$ i686-w64-mingw32-dlltool -d user32.def -k -l libuser32.a
$ i686-w64-mingw32-gcc pure0.c pure1.c pure2.c -o pure.exe -mwindows
-nostartfiles -nostdlib libkernel32.a libuser32.a
-Wl,--disable-runtime-pseudo-reloc -W -Wall -Os -flto
objdump on compiled pure.exe shows:
$ objdump -d pure.exe
pure.exe: file format pei-i386
Disassembly of section .text:
00401000 <_WinMainCRTStartup>:
401000: 55 push %ebp
401001: 31 c0 xor %eax,%eax
401003: b9 11 00 00 00 mov $0x11,%ecx
401008: 89 e5 mov %esp,%ebp
40100a: 57 push %edi
40100b: 8d 7d b4 lea -0x4c(%ebp),%edi
40100e: 83 ec 64 sub $0x64,%esp
401011: f3 ab rep stos %eax,%es:(%edi)
401013: 8d 45 b4 lea -0x4c(%ebp),%eax
401016: 89 04 24 mov %eax,(%esp)
401019: ff 15 4c 40 40 00 call *0x40404c
40101f: 31 d2 xor %edx,%edx
401021: 31 c9 xor %ecx,%ecx
401023: 50 push %eax
401024: 89 54 24 0c mov %edx,0xc(%esp)
401028: c7 44 24 08 00 20 40 movl $0x402000,0x8(%esp)
40102f: 00
401030: c7 44 24 04 06 20 40 movl $0x402006,0x4(%esp)
401037: 00
401038: 89 0c 24 mov %ecx,(%esp)
40103b: ff 15 54 40 40 00 call *0x404054
401041: 8b 7d fc mov -0x4(%ebp),%edi
401044: 31 c0 xor %eax,%eax
401046: 83 ec 10 sub $0x10,%esp
401049: c9 leave
40104a: c3 ret
0040104b <___main>:
40104b: c3 ret
0040104c <_GetStartupInfoA@4>:
40104c: ff 25 4c 40 40 00 jmp *0x40404c
401052: 90 nop
401053: 90 nop
00401054 <_MessageBoxA@16>:
401054: ff 25 54 40 40 00 jmp *0x404054
40105a: 90 nop
40105b: 90 nop
0040105c <__CTOR_LIST__>:
40105c: ff (bad)
40105d: ff (bad)
40105e: ff (bad)
40105f: ff 00 incl (%eax)
401061: 00 00 add %al,(%eax)
...
00401064 <__DTOR_LIST__>:
401064: ff (bad)
401065: ff (bad)
401066: ff (bad)
401067: ff 00 incl (%eax)
401069: 00 00 add %al,(%eax)
...
Function GetStartupInfoA() was called only in _winshowcmd() function and return
value of _winshowcmd() function is unused. So _winshowcmd() function is marked
as pure and its return value is unused then gcc could completely optimize out
_winshowcmd() function call in LTO build. But objdump on above example shows
that gcc had not dropped _winshowcmd() function and it is there.
Changing __attribute__((pure)) to __attribute__((const)) for _winshowcmd()
function does not help gcc to optimize out the _winshowcmd() function call.