Some effects of coding styles on optimizations
Authors: Daniel Burton
Venue: Proceedings of the conference on TRI-ADA '90 (1990)

This presentation examines the effect of a few simple Ada coding styles on compiler optimizations. The discussion should make the user aware of the impact these styles have on the ability of an Ada compiler to optimize the resulting programs. During the presentation, simple examples of each style will be presented, along with the impact of each on a compiler's ability to optimize a program. First, the use of separates adversely affects the performance of optimizations across calls to the separate. Because no analysis of the separate unit can be performed before a use of the separate unit is seen, worst case assumptions must be made about the effects of the separate unit. This inhibits optimizations such as constant folding, common subexpression elimination, and equivalence propagation from occurring across a call to a separate unit. Thus, although separates may be attractive from a development point of view, their use may have a negative impact on system performance. Another feature that limits what an optimizer can do is access types. Because an access type may be assigned a value using unchecked conversion, a compiler is unable to accurately determine what objects have actually been modified when an assignment is made to a dereferenced access type value. For this reason, a compiler must limit optimizations across assignments to a dereferenced access variable. Being aware of how access types limit optimization, a user might be better off to use an alternate method such as the use of array structures where compiler optimizations are not limited. Package machine_code is another area that limits a compiler's ability to optimize across procedure calls. Often, to very efficiently put a few assembly language instructions into a program, a programmer will use package machine_code. The resulting code may not be as efficient as desired. One reason is that the compiler has no semantic information on the effects of the assembly code and therefore cannot optimize across the inserted assembly code. An alternate approach that can be used when a limited set of single assembly instructions is needed is to provide a package of built-in functions whose effect is known to the compiler. Each built-in function is mapped to a single assembly instruction by the compiler. Using this technique, the compiler is not forced to abandon optimization across calls to the built-in functions. A fourth area that limits a compiler's ability to produce efficient code involves the use of unconstrained types. Typically, to make a routine more general, a user may define a procedure that operates on unconstrained array types, even though the procedure may only be used with one type. However, at compile time the compiler does not know the actual sizes of the array objects with which the routine will be called; the compiler must therefore produce, at runtime, code that determines the array sizes. This can be expensive in terms of execution time. A better alternative, in terms of runtime performance, is to write a generic unit and instantiate it for constrained types. This allows the compiler to determine at compile time all the necessary information on sizes to produce very efficient code for each instantiation. This does imply that the efficiency comes at the cost of enlarged code space. The initialization of record and array objects is yet another area that affects runtime performance. Single field initializations do not result in good runtime performance or code size. Significantly smaller code size and faster runtime performance can be obtained if the initialization is done with a static aggregate. This is possible because the compiler can create the aggregate at compile time; at runtime the object can be initialized to the record by a very efficient block copy of the compiler-generated aggregate or array object. However, if the aggregate is not static, i.e., the compiler cannot completely evaluate it at compile time, then aggregate initialization can be expensive.