Dead Code Elimination

On first reading, the term ''dead code elimination'' sounds quite violent. On second reading, it seems to be somewhat contradictory. After all, how can code that is already dead be eliminated? Only when the term is examined for a third time does it become apparent that it refers to an optimization feature in which program sections that can never execute are eliminated from code generation to reduce the size of the assembler code.

How does dead code accumulate in a program? It would be normal to expect programmers to give some thought as to how their programs should run. And why should they waste their time writing superfluous program fragments? This is indeed true for simple programs, but the situation may well be different for larger chunks of code that define a range of constants for specific program purposes. Elimination of dead code is one of several important aspects when compiling C code for the architecture-independent memory model discussed in detail in Chapter 3 (this model provides a uniform interface to the various processors supported by the kernel). To understand how this optimization works take a look at the following short example:

printf("x

Without optimization, the following assembler code would be generated:

.file "dead.c" .section .rodata

.string "x is less than 10!\n" .align 32

.string "x is greater than or equal to 10!\n" .text

.globl main

.type main,@function main:

pushl %ebp

is greater than or equal to 10!\n");

movl %esp, %ebp subl $8, %esp andl $-16, %esp movl $0, %eax subl %eax, %esp movl $23, -4(%ebp)

jg ,L2

call printf jmp .L3

call printf leave ret

.size main,.Lfe1-main

Because the value of 23 assigned to x cannot change prior to the if query, the result of the query is obvious — the second program branch (the else clause) always executes, and this renders explicit computation to determine whether x is less than or greater than 23 superfluous. The code for the first query is therefore a dead program section because it can never be reached. Therefore, the compiler need not compile the corresponding statements. But there is also a further benefit because the string constant is less than 10! no longer needs to be stored in the object file. In addition to speeding program execution, optimization also reduces the size of the generated code. Omission of the character string from the object file is a relatively new optimization feature only supported by GCC Version 3 and higher.

The optimized assembler code looks like this: .file "dead.c"

.section .rodata.str1.32,"aMS",@progbits,1

.align 32

.string "x is greater than or equal to 10!" .text

.p2align 4,,15

.globl main

.type main:

pushl movl subl andl movl call movl popl ret

.size .ident main,@function %ebp

%ebp, %esp %ebp main,.Lfe1-main "GCC: (GNU) 3.2.1

Once you understand this optimization feature, the use of scanf to read the x variable in the previous example becomes clearer. If the value of x cannot change prior to the if query, dead code elimination is also applied to this program. Alternatively, it would have been possible to declare the variable as volatile. This informs the compiler that the value of the variable can be modified by uncontrollable side effects (such as interrupts), and this suppresses some kinds of optimization — including dead code elimination.

Continue reading here: C15 Inline Functions

Was this article helpful?

+1 -1