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.