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,