[gnu.gcc] New option: -fdisable-inlines

rfg@ICS.UCI.EDU (12/09/89)

[Note: This is a long message, but if you persevere, there is a short
set of patches to implement a -fdisable-inlines option at the end.]

-----------------------------------------------------------------------
The ability to do function inlining (in GCC and G++) is terrific right?

Well, not always.

There are several reasons why you might want to totally avoid doing any
function inlining.

First and foremost, there is the issue of debugging.  Assume that you have
some existing code (like libg++) that is sprinkled with inline's all over
the place.  Such code is inheriently harder to debug for several reasons.
First, try setting a breakpoint on an inlined function.  You definitely
will not get the desired effect.  Second, if you don't really know all of
the ins and out of the code you are debugging, and if you are single stepping
down at the instruction level (using "si" in GDB) then you may be quite
surprized to see a mass of unexpected instructions at the point where it
would seem (intuitively, from looking at the source code) that you ought
to just see some parameter pushes and then a subroutine-call instruction.
Third, (and this is *not* an issue that *everybody* even cares about)
some object file formats (e.g. COFF) do not provide proper support for
debugging information for inlined functions.

A second reason to avoid inlining is that you may want the results of
compilation to be "optimized" for minimal space usage (rather than
for minimal time usage).  You may wish to minimize code space (at the
expense of a minor speed penalty) if you are targeting for an embedded
system (or any other system) in which space is at premium.  A related
(but minor) consideration is that you could reduce disk space used to
hold the object files if you totally avoided inlining.

Third, you may want to avoid inlining entirely simply because you are
compiling a version of your program for test purposes *only* (i.e. you
are also using -g and you plan to debug the generated program and you
plan to generate an "optimized" version later).  In such cases, you
would probably rather minimize compile time (at the possible expense
of execution efficiency).  Less inlining generally means less compile
time.

Finally, you may want to avoid inlining because you want to do profiling
of your program.  If inlining occurs, profiling results will *not*
directly reveal that a lot of time is spent executing one particular
(inlined) function because the inlining causes this function to be
scattered around in several different places.

----------------------
So if we accept that users may want to "disable" inlining, how can that
be done?  (Assume that we do *not* want to force the user to do lots of
manual editing on *all* of the source files.)

Well, you could try compiling with -Dinline="" but (for g++ at least)
that just doesn't work.  The problem is that the "inline" keyword also
has the effect of declaring the functions to which it applies as having
"static" linkage.  Thus, if you have inline functions inside of include
files (which is common usage) and if you compile your source files with
-Dinline="" then you will get multiple *extern* (i.e. publically visible)
copies of the (formerly) inline functions.  Thus, the linker will later
give you "multiply defined" errors.

So you could instead try compiling with -Dinline=static but this also fails
to work.  There are two problems here.  First, some people (e.g. Doug Lea
of libg++ fame) sometimes declare functions as "static inline" or as
"inline static".  (In fact there may be important reasons for doing this
in the case of C++ member functions.)  Anyway, if -Dinline=static is
used they you will get syntax errors from the resulting "static static"
function declarations.  The second problem is that the semantics of "static"
for C++ member functions has nothing to do with "linkage".  Rather, a
"static" member function is a special member function which has no "this"
pointer.

-------------------
So the bottom line is that there is no easy way to get the preprocessor to
disable all inlining for an existing batch of code that we wish to avoid
making lots of editorial changes to.

Can this problem be solved in the compiler?  Sure.  The following (trivial?)
patches implement a new option (-fdisable-inlines) which can be used to
disable *all* inlining while leaving functions declared as "inline" with
"static" linkage.  This option has already proved useful (for me at least)
in debugging a libg++ problem.

--------------------
The next thing on my agenda is to get GDB to understand (if it does not
already) that if I request a breakpoint on a given function, and if that
function exists as several duplicate ("static" linkage) functions in the
executable being debugged, then it (GDB) may need to set several different
breakpoints (one in each of the duplicate functions) in order to properly
satisfy my request.

Oh, well.  One thing at a time.

-------------------
The following patches were made from a hacked up version of G++ 1.36.1,
but I checked, and they seem to go into GCC 1.36 smoothly also.  Nonetheless,
your lines number will undoubtedly vary.

Note that the way these patches work, if a user specifies -fdisable-inlines
*and* -finline-functions, then the -fdisable-inlines will win out, and no
inlining will occur.

Enjoy,

// rfg

diff -rc2 1.36.1.1/expr.c 1.36.1.2/expr.c
*** 1.36.1.1/expr.c	Thu Dec  7 09:46:48 1989
--- 1.36.1.2/expr.c	Fri Dec  8 00:43:17 1989
***************
*** 4415,4421 ****
  {
  #ifdef EXIT_IGNORE_STACK
!   if (!flag_omit_frame_pointer && EXIT_IGNORE_STACK
!       && ! TREE_INLINE (current_function_decl)
!       && ! flag_inline_functions)
      pending_stack_adjust = 0;
  #endif
--- 4415,4422 ----
  {
  #ifdef EXIT_IGNORE_STACK
!   if (!flag_omit_frame_pointer
!       && EXIT_IGNORE_STACK
!       && (flag_disable_inlines
!           || (! TREE_INLINE (current_function_decl) && ! flag_inline_functions)))
      pending_stack_adjust = 0;
  #endif
diff -rc2 1.36.1.1/flags.h 1.36.1.2/flags.h
*** 1.36.1.1/flags.h	Thu Dec  7 09:44:14 1989
--- 1.36.1.2/flags.h	Thu Dec  7 09:44:14 1989
***************
*** 182,185 ****
--- 182,193 ----
  extern int flag_inline_functions;
  
+ /* Nonzero means prevent all functions from ever being inlined anywhere.
+    This may be useful when debugging if you want to set breakpoints on
+    functions which might otherwise get inlined.  Note that this option
+    overrides both flag_inline_functions and any explicit "inline" keywords
+    in the user's code.  */
+ 
+ extern int flag_disable_inlines;
+ 
  /* Nonzero for -fkeep-inline-functions: even if we make a function
     go inline everywhere, keep its defintion around for debugging
diff -rc2 1.36.1.1/toplev.c 1.36.1.2/toplev.c
*** 1.36.1.1/toplev.c	Thu Dec  7 09:45:47 1989
--- 1.36.1.2/toplev.c	Fri Dec  8 00:39:16 1989
***************
*** 313,316 ****
--- 313,324 ----
  int flag_inline_functions;
  
+ /* Nonzero means prevent all functions from ever being inlined anywhere.
+    This may be useful when debugging if you want to set breakpoints on
+    functions which might otherwise get inlined.  Note that this option
+    overrides both flag_inline_functions and any explicit "inline" keywords
+    in the user's code.  */
+ 
+ int flag_disable_inlines = 0;
+ 
  /* Nonzero for -fkeep-inline-functions: even if we make a function
     go inline everywhere, keep its defintion around for debugging
***************
*** 364,367 ****
--- 372,376 ----
    {"function-cse", &flag_no_function_cse, 0},
    {"inline-functions", &flag_inline_functions, 1},
+   {"disable-inlines", &flag_disable_inlines, 1},
    {"keep-inline-functions", &flag_keep_inline_functions, 1},
    {"syntax-only", &flag_syntax_only, 1},
***************
*** 1506,1510 ****
  
        /* If requested, consider whether to make this function inline.  */
!       if (flag_inline_functions || TREE_INLINE (decl))
  	{
  	  TIMEVAR (integration_time,
--- 1515,1521 ----
  
        /* If requested, consider whether to make this function inline.  */
!       if (flag_disable_inlines)
!         TREE_INLINE (decl) = 0;
!       else if (flag_inline_functions || TREE_INLINE (decl))
  	{
  	  TIMEVAR (integration_time,