N766 Inlining Issues N766 J11/97-130 --------------- J11/97-130 23 September 1997 Tom MacDonald tam@cray.com Introduction ------------ Function inlining was added to the C9X Draft at the London meeting. Some changes were made to the base proposal. This proposal identifies some of those changes and explores some alternatives that might provide a better inlining feature. Issue #1 The constraint below: 6.5.4 Function-specifiers [#4] An inline definition (see below) of a function with external linkage shall not define an object of static storage duration or refer to an object with internal linkage. Is needlessly strict. There are two cases where the translator must handle static data: string literals and __func__. Since the translator must "correctly" handle some objects with static storage duration that are not modifiable, there appears to be no additional burden on the implementor if all non-modifiable objects with static storage duration are allowed inside inline definitions. Seems like the "refer to an object with internal linkage" is not strict enough. The same problem exists for functions with internal linkage. The following modification is proposed: ------------------------------------------------------------------------ | | | An inline definition (see below) of a function with external linkage | | shall not contain a definition of an object with static storage | | duration that can be modified, and shall not contain a reference | | to an identifier with internal linkage. | | | ------------------------------------------------------------------------ These words allow: inline double circum(float radius) { static const double pi = 3.14159; // OK - pi is not modifiable return 2.0 * pi * radius; } and forbid: static int funny(void) { return __LINE__; } inline int bad(void) { return funny(); } // Error - funny has // internal linkage -------------------------------------------------------------------- Issue #2 The constraint below: 6.5.4 Function-specifiers [#5] A file scope declaration without inline for a function with external linkage shall not follow a definition with inline of that function. contains the most significant change to the original inline proposal and was caused by a concern for one pass compilers. The first problem is that the new words seem to disallow the following: extern int add(int x, int y); inline int add(int x, int y) {return x + y;} extern int add(int x, int y); // Error? Also, the following is disallowed: int add(int x, int y) { return x + y; } inline int add(int, int); extern int add(int x, int y); // Error? because a file scope declaration without inline follows a definition with inline. Doesn't seem like this was intended. The following words make it an error only if it's an inline definition up to that point. ----------------------------------------------------------------------- | | | [#5] If the definition of a function with external linkage has the | | inline specifier and is not preceded by a file scope declaration | | of that function without the inline specifier, then it shall not | | be followed by such a declaration. | | | ----------------------------------------------------------------------- Forbidding: inline int add(int x, int y) {return x + y;} extern int add(int x, int y); -------------------------------------------------------------------- Final Issue: The belief seems to be that single pass compilers are burdened and forced to keep a copy of the body of any inline function around. Thus the programmer must write: extern int add(int x, int y); inline int add(int x, int y) {return x + y;} instead. This turns out to be quite an onerous burden to place on the programmer. If we can show that single pass implementations are not burdened by the original rules, then perhaps this can be relaxed. It's a burden on programmers because they now bump into situations where they have to write: extern int add(int x, int y); #include "common_defs.h" When enhancing existing programs it is far more natural to write: #include "common_defs.h" extern int add(int x, int y); (Granted there are other work-arounds but I think most can agree that if this restriction is unnecessary, then inline is easier to use). The belief seems to be that a single pass implementation would have to needlessly keep a copy of the inline function around. If an implementation performs inlining, then a copy is kept around anyway in case an inlining opportunity arises. If the implementation performs no inlining, then the fear is that an inline definition is carried around internally until the end of the translation unit just in case an external definition appears, and the implementation must materialize a definition. One easy scenario is to write the internal representations of the inline candidates to a file. If an external definition is required, the compiler retrieves the internal representation from the file and generates code. The question becomes, is this too big of a burden to place on an implementation when compared to the burden placed on the programmer? -------------------------------------------------------------------- Nits: The Draft talks about: "The declaration of an identifier for a function" and not about "a function declaration" (which seems to include pointers to functions, etc.). Therefore, the following tweaks are recommened. [#3] Function-specifiers shall be used only in function declarations. ^^^^^^^^^^^^^^^^^^^^^^^^ with function declarators or possibly: [#3] Function-specifiers shall be used only in function declarations. ^^^^^^^^^^^^^^^^^^^^^^^^ in the declaration of an identifier for a function Better specification words: [#6] The inline function specifier shall not appear in a declaration of main. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ be used in a declaration of the identifier /main/ if the identifier has external linkage. The following seems better specified: [#7] A function declaration with an inline function ^^^^^^^^^^^ declared specifier declares an inline function. ^^^^^^^^ is Typo and a tweak: [#8] Any function with internal linkage can be an inline function. For a function wtih external linkage, the ^^^^ with following restrictions apply. If a function is declared with an inline function specifier, then it shall also be defined in the same translation unit. If all of the file ^ the definition and scope declarations for a function in a translation unit include the inline function specifier, then the definition in that translation unit is an inline definition. An inline ........ Typo: [#9] The declaration of an inline function can result in either an external definition, or a definition available for use only within the translation unit. A file scope declaration without inline creates an external definition. The following example shows an entier translation unit. ^^^^^^ entire Rewrite of para. 10: From: [#10] Note that the declaration of inline function fahr results in the creation of an external definition, but the inline definition of cels requires an external definition in another translation unit. To: [#10] Note that the definition of fahr is an external definition because fahr is originally declared without the inline keyword, but the definition of cels is an inline definition. Because there is a call to cels, an external definition of cels in another translation unit is still required by 6.7.