Background:
===========

In the Linux kernel one often sees the following idiom:

static void foo_func(void)
{
}
static struct foo_type foo_struct = {
        .foo_call = foo_func,
};
some_init_func(...)
{
        register_foo(&foo_struct);
}
some_exit_func(...)
{
        unregister_foo(&foo_struct);
}

The point is that {un}register_foo() may be a configuration-dependent
functions that may either be externally-implemented-in-another-module
or, if that configuration item is turned off, resolve to empty stubs
defined in a header itself. [ The empty stubs may be do-nothing macros,
or often static inline functions with empty bodies. ]

The expectation is that, when defined as empty stubs, because there is
effectively no reference left to the static foo_struct and, transitively,
to the static foo_func, the optimizer must just optimize them away
completely.

Problem:
========

The expectation holds true, when the empty stubs are defined as empty
do {} while (0); macros, but not if defined as empty "static inline
int ... {return 0;}" functions.

Testcase:
=========

I think the below (with given commentary) is self-explanatory:

/* gcc -Wall -Os -S */

struct abc {
        void (*abc_call)(void);
};

/*
 * Use only any one of the three definitions below at a time:
 *
 * 1. nothing optimized away. Good.
 * 2. call_func() _not_ optimized away, but struct xyz is. gcc disappoints.
 * 3. both call_func() and struct xyz optimized away. Nice.
 */

/* 1 */
/*extern int do_register(struct abc *xyz);*/

/* 2 */
static inline int do_register(struct abc *xyz)
{
        return 0;
}

/* 3 */
/*#define do_register(xyz)      do { (void)(xyz); } while (0)*/

static void call_func(void)
{
}

static struct abc xyz = {
        .abc_call = call_func,
};

void func(void)
{
        do_register(&xyz);
}

Assembler for problematic case:
===============================

        .file   "xyz.c"
        .text
        .type   call_func, @function
call_func:
        pushl   %ebp
        movl    %esp, %ebp
        popl    %ebp
        ret
        .size   call_func, .-call_func
.globl func
        .type   func, @function
func:
        pushl   %ebp
        movl    %esp, %ebp
        popl    %ebp
        ret
        .size   func, .-func
        .ident  "GCC: (GNU) 4.1.1 20070105 (Red Hat 4.1.1-51)"
        .section        .note.GNU-stack,"",@progbits

As you can see, there is nobody referencing the static call_func()
in this object file, and being static, there can be none in others
either. Why did we fail to just elid it away completely?

[ Interestingly, the equally-unreferenced static struct xyz _did_
  get elided away. But not so the static function. ]


-- 
           Summary: Optimizer fails to elid away unreferenced static
                    function
           Product: gcc
           Version: 4.1.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: rtl-optimization
        AssignedTo: unassigned at gcc dot gnu dot org
        ReportedBy: satyam at infradead dot org
  GCC host triplet: i386-redhat-linux
GCC target triplet: i386-redhat-linux


http://gcc.gnu.org/bugzilla/show_bug.cgi?id=33172

Reply via email to