https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70604
Bug ID: 70604 Summary: switch statement optimization creates dead code Product: gcc Version: 6.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c Assignee: unassigned at gcc dot gnu.org Reporter: jpoimboe at redhat dot com Target Milestone: --- Created attachment 38226 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=38226&action=edit iscsi_target.i.gz The linux kernel has a new tool named "objtool" which follows all possible code paths for every .o file, looking for abnormalities. In rare cases it has stumbled on a gcc optimization issue related to switch statements, which leaves some dead code (unreachable instructions) laying around, resulting in increased code size and confusing assembler code. Note: For some reason the likelihood of seeing this problem seems to have diminished from gcc 5.3.1 to gcc 6.0. We've seen the problem in three separate object files in the kernel with gcc 5.3.1, but with gcc 6.0 there's only one known occurrence of this issue. Further, even in that one case, the size of the dead code has decreased from several instructions in 5.3.1 to only a single unreachable instruction in 6.0. Here are the relevant assembler code excerpts, using gcc 6.0: iscsit_handle_task_mgt_cmd: [...snip...] jmp *.L6212+64(%rip) # .section .rodata .align 8 .align 4 .L6212: .quad .L6211 .quad .L6070 .quad .L6214 .quad .L6214 .quad .L6214 .quad .L6214 .quad .L6082 .quad .L6089 .quad .L6096 .text [...snip...] jmp .L6196 # .L6211: movl $8, %esi #, _315 .L6213: movq $.LC48, %rdi #, There's an indirect jump instruction in .text, along with a jump table in .rodata, which is a common pattern for switch statements. But this one's a little different than the normal pattern: the indirect jump is hard-coded to use a single entry in the jump table. The other jump table entries are unused. Further, the code referenced in one of the entries (.L6211) is dead code and completely unreachable from anywhere in the function. Note that the -fsanitize=unreachable option is enabled, but this seems unrelated: __builtin_unreachable() doesn't appear to have been used for this code path, and the unreachable code block doesn't have a call to __ubsan_handle_builtin_unreachable. So to summarize, the issues are: 1) dead code in .text (in this case, the function only has one unreachable instruction) 2) mostly unused (and completely unnecessary) jump table in .rodata 3) unnecessary indirect jump (a direct jump could be used instead) $ gcc -v Using built-in specs. COLLECT_GCC=/usr/bin/gcc COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/6.0.0/lto-wrapper Target: x86_64-redhat-linux Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,objc,obj-c++,fortran,ada,go,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --disable-libgcj --with-isl --enable-libmpx --enable-gnu-indirect-function --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux Thread model: posix gcc version 6.0.0 20160311 (Red Hat 6.0.0-0.17) (GCC) $ gcc -Wp,-MD,drivers/target/iscsi/.iscsi_target.o.d -nostdinc -I./arch/x86/include -Iarch/x86/include/generated/uapi -Iarch/x86/include/generated -Iinclude -I./arch/x86/include/uapi -Iarch/x86/include/generated/uapi -I./include/uapi -Iinclude/generated/uapi -include ./include/linux/kconfig.h -D__KERNEL__ -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -Werror-implicit-function-declaration -Wno-format-security -std=gnu89 -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -m64 -falign-jumps=1 -falign-loops=1 -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -funit-at-a-time -maccumulate-outgoing-args -DCONFIG_AS_CFI=1 -DCONFIG_AS_CFI_SIGNAL_FRAME=1 -DCONFIG_AS_CFI_SECTIONS=1 -DCONFIG_AS_FXSAVEQ=1 -DCONFIG_AS_SSSE3=1 -DCONFIG_AS_CRC32=1 -DCONFIG_AS_AVX=1 -DCONFIG_AS_AVX2=1 -DCONFIG_AS_SHA1_NI=1 -DCONFIG_AS_SHA256_NI=1 -pipe -Wno-sign-compare -fno-asynchronous-unwind-tables -fno-delete-null-pointer-checks -O2 --param=allow-store-data-races=0 -Wframe-larger-than=1024 -fno-stack-protector -Wno-unused-but-set-variable -fomit-frame-pointer -fno-var-tracking-assignments -Wdeclaration-after-statement -Wno-pointer-sign -fno-strict-overflow -fconserve-stack -Werror=implicit-int -Werror=strict-prototypes -Werror=date-time -Werror=incompatible-pointer-types -DCC_HAVE_ASM_GOTO -fsanitize=shift -fsanitize=integer-divide-by-zero -fsanitize=unreachable -fsanitize=vla-bound -fsanitize=null -fsanitize=signed-integer-overflow -fsanitize=bounds -fsanitize=object-size -fsanitize=returns-nonnull-attribute -fsanitize=bool -fsanitize=enum -fsanitize=alignment -Wno-maybe-uninitialized -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(iscsi_target)" -D"KBUILD_MODNAME=KBUILD_STR(iscsi_target_mod)" -c -o drivers/target/iscsi/iscsi_target.o drivers/target/iscsi/iscsi_target.c This is with upstream Linux tag v4.6-rc2. I've attached a gzipped version of the .i file and will also attach the Linux .config file.