PowerAda Descriptions of Optimizations
This section discusses the specific optimizations that the compiler performs. This section describes only the more significant optimizations, and emphasizes ways that you can write code at a high level and allow the compiler to do the work of making the resulting application as fast and as small as possible.
- 1 Inline Expansion
- 2 Using Pragma INLINE
- 3 Using Automatic Inlining
- 4 Using Transitive Inlining
- 5 Performance Tradeoffs
- 6 Requirements for Inlining
- 7 Unit Dependencies Created by Inlining
- 8 Callable Bodies for Visible Inline Subprograms
- 9 Inline Body Dependencies
- 10 Evaluation of Conditional Expressions
- 11 Other Optimizations
One of the optimization techniques the compiler uses is inline expansion, where a call to a subprogram is replaced by a copy of the subprogram itself. The compiler provides two ways to control inline expansion:
- You can insert pragma INLINE designations in the source. These designations indicate the subprograms to be inlined, and the compiler expands them inline.
- The compiler automatically inlines all subprograms that are only called from one place in a program and are not visible outside the compilation unit you are optimizing. These subprograms are inlined whether or not you have applied pragma INLINE to them.
Using Pragma INLINE
The optimizer supports inlining of calls to subprograms that the user identifies through the pragma INLINE. The pragma must be placed in the same declarative region as the declaration of the subprogram to be inlined and must follow the subprogram declaration. For example, in the package declaration for package DRAG_CALC below, the directive to inline function DRAG_COEFF is placed after the declaration of the function:
package DRAG_CALC is
type PLANE_TYPE is (PL_1, PL_2, PL_3, PL_4, PL_5, PL_6);
function DRAG_COEFF (PLANE: PLANE_TYPE) return FLOAT;
pragma INLINE (DRAG_COEFF);
Using Automatic Inlining
The compiler also supports automatic inlining, where it automatically inlines subprograms that are called from only one place and that are not visible outside the compilation unit you are optimizing. This allows you to use certain coding practices and still avoid the overhead of many subprogram calls. For instance, you can separate out a functional block of code as a subprogram to keep the size of the caller down to a manageable level. Optimization helps to eliminate the penalty for this style of structured design. Elimination of the call overhead by inlining is especially beneficial when the subprogram is called inside a loop that repeats many times.
Using Transitive Inlining
The inlining of subprograms is transitive. For example, if inlined subprogram A is called by inlined subprogram B, and B is called by subprogram C, then optimization results in A being inlined in B and the result being inlined in C.
The one exception to this rule is that subprograms will not be automatically inlined into a subprogram that you have marked for inlining using pragma INLINE. Automatic inlining is inhibited in this case to ensure that you have full control over the inlining process. It prevents any unexpected and undesired size overhead introduced by the automatic inlining of a called subprogram.
Because inline expansion can increase the speed of your program but can also make it larger, you may need to consider which is more important to you, execution speed or program size.
To minimize size overhead, try to inline only very small subprograms. That way the inline expansion will not take much more space than the subprogram call it replaces. If you use pragma INLINE without considering these issues, the compiler will honor inline subprogram designations regardless of code space costs.
Note: The best candidates for inlining are subprograms called many times from a relatively small number of places. Each discrete call to the inlined routine generates all of the code necessary to replicate that routine in place. If the candidate routine is called from very many places and/or the code to be moved is very large, the final size of the code produced could be unacceptably large.
Requirements for Inlining
In order for the compiler to expand a subprogram inline, the following conditions must be met:
- The subprogram must be designated in a pragma, or be called from only one place and not visible outside the containing compilation unit, and so subject to automatic inlining.
- The units that call the subprogram must be optimized using the
- You must compile the unit that contains the body of an inlined subprogram before compiling any units that call the inlined subprogram. The inline expansion can only occur if the subprogram to be inlined is already compiled.
If any of these conditions are not met, inline expansion does not take place and the compiler emits a warning and generates code to make a normal call to a non-inlined copy of the subprogram.
Unit Dependencies Created by Inlining
Because of the nature of the Ada language, inlining may create new unit dependencies. This section describes the consequences of inlining certain subprograms within a given application.
Callable Bodies for Visible Inline Subprograms
The compiler always generates a body for a subprogram that uses pragma INLINE if that subprogram is externally visible. The reason is that a subprogram may have been compiled before an inline subprogram that it calls. You can prevent the optimizer from generating a callable body by declaring the subprogram where it is not externally visible (for instance, in the body of a package).
Inline Body Dependencies
When a subprogram call is expanded inline, a dependency is created between the unit body where the expansion occurs and the unit that contains the inlined body. Recompiling the inline body will cause the unit where the expansion occurred to become obsolete because a copy of the code for the old body will still be present in the unit that made the call to the inlined subprogram. Unless you subsequently recompile the unit containing the inline expansion, the compiler will detect an inconsistency and stop when an attempt is made to link the main program.
Evaluation of Conditional Expressions
The optimizer tries to evaluate conditional expressions at compile time. If the conditional expression can be reduced to a value of TRUE or FALSE then the optimizer can eliminate unnecessary testing or branching.
If the conditional expression occurs in a control flow context, then it is replaced by a direct branch to the location selected by the value of the expression. For instance, within an if-then-else clause that contains no label statements, if the conditional expression evaluates to TRUE then the ELSE part of the statement will never be executed and the compiler can eliminate it.
If the conditional expression occurs in an assignment, for example X := Y < Z, then the value of the expression determined by the optimizer is placed as the right hand side of the assignment. Note that tests conducted as part of or and and will not be eliminated where the change would violate the rules of Ada syntax. For instance, any tests that might have side-effects will not be removed.