Although inlining removes the cost of the function call and return instructions, these are often small savings. The major savings often come from the additional optimizations that become possible on the inlined function body: for example, a constant passed as an argument can often be propagated to all instances of the matching parameter. Inlining may increase the size of the generated code, though it does not always do so. Inlining may make the generated code slower as well: for instance, by decreasing locality of reference.
Ordinarily, when a function is invoked, control is transferred to its definition by a branch or call instruction. With inlining, control flows directly to the code for the function, without a branch or call instruction.
Some languages (for example, C and C++) support the inline keyword in function definitions. This keyword serves as a hint to the compiler that it should try to inline the function. Compilers use a variety of mechanisms, including hints from programmers, to decide which function calls should be inlined.
In the context of functional programming languages, inline expansion is usually followed by the beta-reduction transformation.
A programmer might inline a function manually through copy and paste programming, as a one-time operation on the source code. However, other methods of controlling inlining (see below) are preferable, because they do not precipitate bugs arising when the programmer overlooks a (possibly modified) duplicated version of the original function body while fixing a bug in the inlined function.
Linkers, as well as compilers, can also do function inlining. When a linker inlines functions, it may inline functions whose source is not available, such as library functions (see link-time optimization). A run-time system can inline function as well. Run-time inlining can use dynamic profiling information to make better decisions about which functions to inline, as in the Java Hotspot compiler.
Here is a simple example of inline expansion performed "by hand" at the source level in the C programming language:
if (x == 0)
return 0;
else
return x - 1;
}
Before inlining:
int f(int y) {
return pred(y) + pred(0) + pred(y+1);
}
After inlining:
int temp = 0;
if (y == 0) temp += 0; else temp += y - 1; /* (1) */
if (0 == 0) temp += 0; else temp += 0 - 1; /* (2) */
if (y+1 == 0) temp += 0; else temp += (y + 1) - 1; /* (3) */
return temp;
}
Note that this is only an example. In an actual C application, it would be preferable to use an inlining language feature such as parameterized macros or inline functions to tell the compiler to transform the code in this way. The next section lists ways to optimize this code.
In the C example in the previous section, optimization opportunities abound. The compiler may follow this sequence of steps:
temp += 0 statements in the lines marked (1), (2) and (3) do nothing. The compiler can remove them.0 == 0 is always true, so the compiler can replace the line marked (2) with the consequent, test += 0 (which does nothing).y+1 == 0 to y == -1.(y + 1) - 1 to y (assuming wraparound overflow semantics)y and y+1 cannot both equal zero. This lets the compiler eliminate one test.The new function looks like:
if (y == 0)
return y; /* or return 0 */
else if (y == -1)
return y - 1; /* or return -2 */
else
return y + y - 1;
}
Typically, compiler developers keep these issues in mind, and incorporate heuristics into their compilers that choose which functions to inline so as to improve performance, rather than worsening it, in most cases.
Furthermore, it is not always possible to inline a subroutine. Consider the case of a subroutine that calls itself recursively until it receives a particular piece of input data from a peripheral. Because the halting problem is undecidable, the compiler cannot generally determine when this process will end, so it would never finish inlining if it was designed to inline every single subroutine invocation. Thus, compilers for languages which support recursion must have restrictions on what they will automatically choose to inline, to avoid getting stuck in an infinite regress.