INLINING CONSIDERATIONS WG14/N616 (X3J11/96-080) Tom MacDonald Cray Research An SGI Company 655F Lone Oak Drive Eagan MN 55121 USA tam@cray.com 3 October 1996 Introduction I started writing a proposal that would provide exact edits to the C9X DRAFT 6 for adding inlining to C. However, it quickly became apparent that the committee needs to list all the issues and then decide if this is a feature we want to provide. I have a list of issues below that is intended to help us decide if we want add inlining to C. Motivation: The motivation for adding inlining to C is performance. There are several implementations that have successfully incorporated some form of inlining into translators (e.g., gcc, Cray, SGI, and C++). The current success of inlining is a good reason for the C committee to examine the potential of this language feature. Although an implementation can perform automatic inlining, direction from the authors of a program is often needed to avoid problems such as excessive translation time, excessive translation memory usage, and excessive size of the resulting executable binary. The author often knows when inlining is a good idea and when it is not needed. The translator all too often has to resort heuristics. Debatable Issues: What does the "inline" keyword mean? The "inline" keyword never changes the behavior of a program, but might allow it to execute faster. That is, an implementation is free to ignore "inline" if it chooses to do so (except that it must successfully translate programs that contain correct usage of the keyword). Thus, "inline" is a hint to the translator (similar to "register"). Any implementation that exploits this hint must preserve the behavior of a strictly conforming program, such that it behaves the same as the same program without the hint. Note: in C++ "inline" is not just a hint. What syntax should be used? There are two possibilities, an inline keyword and an inline pragma. The inline keyword mimics the C++ standard but takes another name away from the user name space. A pragma approach is most likely dependent upon adoption of the new form of pragma (i.e., a unary preprocessor operator). What are the issues with an inline keyword? First, is "inline" a storage-class-specifier, a type-specifier, or a new creature called an inline-specifier? The C++ spec. uses a production called a `function-specifier' and "inline" is one of the alternatives (along with others like "virtual"). The meaning of "inline" in the following examples has to be defined: inline extern void func1(void); // not allowed by some C++ compilers inline void func2(void); inline static void func3(void); What happens if "inline" appears on something other than a function? inline int x; // Error in C++ inline int f(void), (*pf)(void)=f, i; // Error in C++ typedef inline int T; // Error with some C++ compilers T f(void) { return 0; } What about inline composition? inline static int compose(void); static int compose(void) { return 0; } // OK in C++, compose is inline static int comp(void) { return 0; } inline static int comp(void); // OK in C++, comp is inline static int kkk(void) { return 0; } int main(void) { inline int comp(void); // Error with some C++ compilers int k = kkk(); } inline static int kkk(void); // Error with some C++ compilers Exactly when can you add the "inline" specifier? What are the issues with an inline pragma? No standard pragmas exist yet, however the committee might be willing to entertain the notion of a standard pragma if the new form of pragma is adopted. A pragma eliminates some of the issues above. Since inline is a hint that doesn't affect program correctness, the implementation has some liberty with a pragma such as the following: pragma("inline my_func"); When present at function scope, it specifies functions to inline. However, an implementation can easily extend the meaning when present at block scope to mean inline any occurrence of `my_func' in the next statement. All too often programmers want to limit where a function is inlined. The C Standard is not burdened with all the issues about syntax and semantics. A pragma approach does mean that C and C++ have different features for essentially the same functionality. The problem here is, that it is hard to keep up with the changes being made to the C++ Draft. What would it even mean to standardize a pragma? Perhaps something like: 1. A specification for the optional and non-optional pp-tokens. 2. An intended meaning for a pragma of the specified form. 3. A recommendation that no diagnostic (except possibly an "informational" one) be issued for a pragma of the specified form. 4. A recommendation that a pragma of the specified form should not be interpreted to have another, unrelated meaning. Consider two possibilities (one from Cray and the other from SGI): #pragma [no]inline [_CRI] (name[,name...]) #pragma [no]inline [here|routine|global] [(name[,name...])] These are pretty close, without the optional elements, but it would be nice to eliminate gratuitous differences in those. A standard specification based on these might go something like what follows (item 6.c below is there for the sake of argument, because it's there for C++). ==================================================================== 5. Pragma inline syntax #pragma [no]inline [vendor_id] [(name[,name...])] [inline_args] where vendor_id shall be from the implementors namespace, and inline_args is not further specified (but does show where to put implementation-specific extensions like [here|routine|global] or [from "path"]). 6. Pragma inline intended semantics a. The the functions designated by the names (or all functions if none are named) should be considered candidates for inlining. b. The invocations that are inlined are determined by the placement of the directive: Next statement in a block, rest of file outside a block. (Other criteria may be specified in inline_args. but would not be portable.) c. The pragma shall follow the definitions of the named functions, if they are visible. (Paths to other files may be specified in inline_args. but would not be portable.) ==================================================================== 7. Other pragma inline possibilities a. Note that the new form of pragma opens up new possibilities for placement. For example, it could go after the parenthesized parameter list, like a C++ cv-qualifier: static int f(int i) pragma("inline") { ... } The meaning is the same as: static int f(int t) { ... } #pragma inline (f) The advantage is that it looks like part of the function signature, and the name for the function doesn't have to be repeated the name of the function. This also addresses issues above like: inline int f(void), (*pf)(void)=f, i; which can be written as: int f(void) pragma("inline"), (*pf)(void)=f, i; b. A way to supply linkage control, like GNU's interpretation of extern inline, could be: static int f(int i) pragma("inline else extern") { ... } or static int f(int i) { ... } pragma("inline (f) else extern"); This means that if the function can be inlined, then the given definition should be used, else ignore the definition and treat f as externally defined. The meaning is like: #if F_CAN_BE_INLINED static int f(int i) pragma("inline") { ... } #else extern int f(int i); #endif except that the compiler defines F_CAN_BE_INLINED automatically, perhaps even on an invocation-by-invocation basis. The problem of keeping the local and external definitions in sync is still there. The definitions need not actually be identical, but you would probably want the same behavior. That way the local definition could be a specialization of the general library function. Even in this case, it appears the pragma can be legitimately regarded as a pure hint, because if it is macro-defined away, the program behaves (aside from execution time) in the same way as if the local definition were used to inline all invocations.