Civil Rights
Movements, leaders, victories and the continuing fight for equality.
Explore the people, places, events, achievements, struggles and stories that shaped our journey.
Movements, leaders, victories and the continuing fight for equality.
Innovation, patents, science, technology and world-changing contributions.
Pioneers, champions, Negro Leagues, records, activism and excellence.
Meet the people whose lives, choices and achievements shaped the journey.
Black towns, communities, institutions and places where history happened.
Moments that changed communities, movements, institutions and the nation.
Wilmington, North Carolina once had a thriving Black middle class and an elected interracial government. In 1898 white supremacists used violence to overthrow that government, kill Black residents and drive many others from the city.
MORE →Reflects the personal views, recollections, and perspective of the author, Mike Davis.
This is a personal recollection on the Move fire on May 13, 1985
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[1]; 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.
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 false (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. #define DEBUG_MODE 0 then #if DEBUG_MODE...).
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.[2] 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.[3] An algorithm based on static single-assignment form (SSA) appears in the original journal article on SSA form by Ron Cytron et al.[4] Robert Shillingsburg (aka Shillner) improved on the algorithm and developed a companion algorithm for removing useless control-flow operations.[5]
In the above example we eliminate the dead code at compile time. Doing so only allows removal of code that is unconditionally dead and can be shown by the optimizer to be the case. The presence of compilation unit (CU) boundaries hinder the determination of deadness by the optimizer.
Consider, for example, a Unix static library (*.a) which contains a number of object files (*.o). At link-time, the linker ld examines the symbols referenced by other pieces of code and chooses to only include the necessary object files – ones that contain the referenced symbols, both by the original code and by the object files pulled in to satisfy the needs of the original object file. This consists a very coarse-grained version of dead-code elimination.
Object files consist of relatively independent sections that may reference each other. When an object file is broken down into more sections, e.g. with each function and/or variable in their own section (-ffunction-sections -fdata-sections), and if the linker is told to analyze the inter-dependencies in a section-level granularity (--gc-sections), a more complete form of link-time DCE can be achieved. (-fdata-sections may counter-productively inflate the size of the binary by creating more relocation entries.)[6]
The gold standard for link-time DCE is to turn link time into another compile time, i.e. link-time optimization. In this setup, object files contain intermediate representations used by the compiler instead of (or in addition to) machine code. In this way, the optimizer is no longer hindered by CU boundaries since it has access to the entire program, enabling it to prove many more properties of the code that can be used for optimization. This comes with the downside of extended compile times for processing such a large representation of the program.
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.[7][8] 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.[7][8] 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[9][10][11] or dynamic dead-instruction elimination.[12]
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.[13][14][15] However, language implementations doing just-in-time compilation may dynamically optimize for dead-code elimination.[11][16][17]
Although with a rather different focus, similar approaches are sometimes also utilized for dynamic software updating and hot patching.
[…] any of the […] options can be "permanently" excluded at installation time (will also save the memory for the corresponding code excerpts due to our Dynamic Dead Code Elimination), or it can be disabled or enabled at any later time via API functions in case someone wants to keep a user from being able to reboot the machine. […] we are considering to add more synchronous cache flush calls […] Due to our Dynamic Dead Code Elimination method this would not cause any kind of bloating when not needed in a particular target configuration as a particular cache flush call would be included in FreeKEYB's runtime image only if the corresponding disk cache is loaded as well or FreeKEYB was told by command line switches to load the corresponding support.
{{cite web}}: CS1 maint: deprecated archival service (link)
[…] FreeKEYB builds the driver's runtime image at initialization time depending on the type of machine it is being loaded on, the type of keyboard, layout, country and code page used, the type of mouse and video adapter(s) installed, the other drivers loaded on that system, the operating system and the load and relocation method(s) used, the individual features included, and the configuration options specified in the command line. Due to the large number of command line switches and options supported […] (around fifty switches […] with multiple possible settings) there is a high number of feature combinations with uncountable dependencies […] resulting in […] endless number of […] different target images. FreeKEYB's Dynamic Dead Code Elimination technique manages to resolve […] these […] dependencies and […] remove dead code and data […] is not restricted to […] include or exclude a somewhat limited number of modules or whole sub-routines and fix up some dispatch tables as in classical TSR programming, but […] works […] at […] byte level […] able to remove […] individual instructions in the middle of larger routines […] distributed all over the code to handle a particular case or support a specific feature […] special tools are used to analyze the code […] and create […] fixup tables […] automated […] using conditional defines […] to declare the various cases […] not only optional at assembly time but at initialization time […] without the […] overhead of having at least some amount of dead code left in the runtime image […] to keep track of all the dependencies between […] these conditionals, dynamically build and relocate the runtime image, fix up all the references between these small, changing, and moving binary parts […] still allowing to use the tiny .COM/.SYS style […] model […] is done at initialization time […] API to import and export object structures between FreeKEYB and the calling application […] to transparently resize and move them internally […] at runtime […]
{{cite web}}: CS1 maint: deprecated archival service (link)
[…] Lazy evaluation is basically dynamic dead code elimination. […]
{{cite newsgroup}}: CS1 maint: deprecated archival service (link) (NB. Possibly the first public use of the term dynamic dead-code elimination, though only conceptually and with a focus on lazy evaluation in functional languages.)
{{cite web}}: CS1 maint: deprecated archival service (link)
[…] brandneue[s] Feature, der dynamischen Dead-Code-Elimination, die die jeweils notwendigen Bestandteile des Treibers erst zum Installationszeitpunkt zusammenbastelt und reloziert, so daß keine ungenutzten Code- oder Datenbereiche mehr resident bleiben (z.B. wenn jemand ein bestimmtes FreeKEYB-Feature nicht benötigt). […]
{{cite newsgroup}}: CS1 maint: deprecated archival service (link) (NB. This represents the first known implementation of byte-level granular dynamic dead-code elimination for software assembled or compiled ahead-of-time.)
Source: Wikipedia. Article content is retrieved live through the MediaWiki API.
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 computer science, sparse conditional constant propagation (SCCP) is an optimization frequently applied in compilers after conversion to static single assignment form (SSA). It propagates constants, which is the calculation of static values which can be calculated at compile time. Moreover, it can find more constant values, and thus more opportunities for improvement, than separately applying dead code elimination and constant propagation in any order or any number of repetitions. The algorithm operates by performing abstract interpretation of the code in SSA form. During abstract interpretation, it typically uses a flat lattice of constants for values and a global environment mapping SSA variables to values in this lattice. The crux of the algorithm comes in how it handles the interpretation of branch instructions. When encountered, the condition for a branch is evaluated as best possible given the precision of the abstract values bound to variables in the condition. It may be the case that the values are perfectly precise (neither top nor bottom) and hence, abstract execution can decide in which direction to branch. If the values are not constant, or a variable in the condition is undefined, then both branch directions must be taken to remain conservative. Upon completion of the abstract interpretation, instructions which were never reached are marked as dead code. SSA variables found to have constant values may then be inlined at (propagated to) their point of use.
Interprocedural optimization (IPO) is a collection of compiler techniques used in computer programming to improve performance in programs containing many frequently used functions of small or medium length. IPO differs from other compiler optimizations by analyzing the entire program as opposed to a single function or block of code. IPO seeks to reduce or eliminate duplicate calculations and inefficient use of memory and to simplify iterative sequences such as loops. If a call to another routine occurs within a loop, IPO analysis may determine that it is best to inline that routine. Additionally, IPO may re-order the routines for better memory layout and locality. IPO may also include typical compiler optimizations applied on a whole-program level, for example, dead code elimination (DCE), which removes code that is never executed. IPO also tries to ensure better use of constants. Modern compilers offer IPO as an option at compile-time. The actual IPO process may occur at any step between the human-readable source code and producing a finished executable binary program. For languages that compile on a file-by-file basis, effective IPO across translation units (module files) requires knowledge of the "entry points" of the program so that a whole program optimization (WPO) can be run. In many cases, this is implemented as a link-time optimization (LTO) pass, because the whole program is visible to the linker.
In computing, in particular compiler construction, value range analysis is a type of data flow analysis that tracks the range (interval) of values that a numeric variable can take on at each point of a program's execution. The resulting information can be used in optimizations such as redundancy elimination, dead code elimination, instruction selection, etc., but can also be used to improve the safety of programs, e.g. in the detection of buffer overruns. Techniques for value range analysis typically use symbolic analysis extensively. Value range analysis is often implemented in the Intel C++ Compiler and is implemented in GCC.
Before the 1921 destruction of Tulsa’s Greenwood District, Black residents had created a remarkable center of business and community life. The district included stores, professional offices, entertainment venues and homes owned by Black citizens. Understanding Greenwood means learning what was built—not only what was burned.
MORE →The Greenwood District.