Dead-code elimination

Uses your system voice; voice and rate in settings.

Snapshot

≈ 6 min read

Names in other languages

No names in other languages are held for this entry yet.

Article bodies are English and are not machine-translated. A name in another language is not a translation of the article.

In compiler theory, dead-code elimination (DCE, dead-code removal, dead-code stripping, or dead-code strip) is a compiler optimization to remove dead code (code that does not affect the program results). Removing such code has several benefits: it shrinks program size (an important consideration in some contexts); reduces resource usage, such as the number of bytes to be transferred ; and allows the running program to avoid executing irrelevant operations, which reduces its running time. It can also enable further optimizations by simplifying program structure. Dead code includes code that can never be executed (unreachable code) and code that only affects dead variables (written to, but never read again), that is, irrelevant to the program.

In compiler theory, dead-code elimination (DCE, dead-code removal, dead-code stripping, or dead-code strip) is a compiler optimization to remove dead code (code that does not affect the program results). Removing such code has several benefits: it shrinks program size (an important consideration in some contexts); reduces resource usage, such as the number of bytes to be transferred ; and allows the running program to avoid executing irrelevant operations, which reduces its running time. It can also enable further optimizations by simplifying program structure. Dead code includes code that can never be executed (unreachable code) and code that only affects dead variables (written to, but never read again), that is, irrelevant to the program.

Examples

Consider the following example written in C. int foo(void) { int a = 24; int b = 25; // Assignment to dead variable int c; c = a * 4; return c; b = 24; // Unreachable code return 0; } Trivial analysis of the control flow would mark lines 7 and 8 for removal as they are never reached after return c; . If the procedure had a more complex control flow, such as a label after the return statement and a goto elsewhere in the procedure, then a feasible execution path might exist to the assignment to b . Furthermore, simple analysis of the uses of values would then show that the value of b is only written to and never read from. b is declared as a local variable inside foo , so its value cannot be used outside foo . Thus, the variable b is dead and an optimizer can reclaim its storage space and eliminate its initialization. Also, even though some calculations are performed in the function, their values are not stored in locations accessible outside the scope of this function. Furthermore, given the function returns a static value (96), it may be simplified to the value it returns (this simplification is called constant folding), resulting in: int foo(void) { return 96; } Most advanced compilers have options to activate dead-code elimination, sometimes at varying levels. A lower level might only remove instructions that cannot be executed. A higher level might also not reserve space for unused variables. A yet higher level might determine instructions or functions that serve no purpose and eliminate them. A common use of dead-code elimination is as an alternative to optional code inclusion via a preprocessor. Consider the following code. // set DEBUG_MODE to false constexpr bool DEBUG_MODE = false; int main(void) { int a = 5; int b = 6; int c; c = a * (b / 2); if (DEBUG_MODE) { printf("%d\n", c); } return c; } Because the constant DEBUG_MODE will always evaluate to (due to being defined as so), the code inside the if statement can never be executed, and dead-code elimination would remove it entirely from the optimized program. This technique is common in debugging to optionally activate blocks of code; using an optimizer with dead-code elimination eliminates the need for using a preprocessor to perform the same task (e.g. then ...). In practice, much of the dead code that an optimizer finds is created by other transformations in the optimizer. For example, the classic techniques for operator strength reduction insert new computations into the code and render the older, more expensive computations dead. Subsequent dead-code elimination removes those calculations and completes the effect (without complicating the strength-reduction algorithm). Historically, dead-code elimination was performed using information derived from data-flow analysis. An algorithm based on static single-assignment form (SSA) appears in the original journal article on SSA form by Ron Cytron et al. Robert Shillingsburg (aka Shillner) improved on the algorithm and developed a companion algorithm for removing useless control-flow operations.

Dynamic

In practice it is also common for code sections to represent dead or unreachable code only under certain conditions, which may not be known at the time of compilation or assembly. Such conditions may be imposed by different runtime environments (for example different versions of an operating system, or different sets and combinations of drivers or services loaded in a particular target environment), which may require different sets of special cases in the code, but at the same time become conditionally dead code for the other cases. Also, the software (for example, a driver or resident service) may be configurable to include or exclude certain features depending on user preferences, rendering unused code portions useless in a particular scenario. While modular software may be developed to dynamically load libraries on demand only, in most cases, it is not possible to load only the relevant routines from a particular library, and even if this would be supported, a routine may still include code sections which can be considered dead code in a given scenario, but could not be ruled out at compile time, already. The techniques used to dynamically detect demand, identify and resolve dependencies, remove such conditionally dead code, and to recombine the remaining code at load or runtime are called dynamic dead-code elimination or dynamic dead-instruction elimination. Most programming languages, compilers and operating systems offer no or little more support than dynamic loading of libraries and late linking, therefore software utilizing dynamic dead-code elimination is very rare in conjunction with languages compiled ahead-of-time or written in assembly language. However, language implementations doing just-in-time compilation may dynamically optimize for dead-code elimination. Although with a rather different focus, similar approaches are sometimes also utilized for dynamic software updating and hot patching.

See also

Redundant code Simplification (symbolic computation) Partial-redundancy elimination Conjunction elimination Dynamic software updating Dynamic coupling (computing) Self-relocation Software cruft Tree shaking – Bundle-time DCE for dynamic languages, analogous to link-time DCE Post-pass optimization Profile-guided optimization Superoptimizer Function multi-versioning

References

Further reading

External links

Text derived from https://en.wikipedia.org/w/index.php?oldid=1374925632 under CC BY-SA 4.0; modifications have been made.

Sources cited in this revision

Extracted from the cited revision's reference blocks; absent citations are not invented.