[gcc] ライブラリ開発者は gccの「Declaring Attributes of Functions」を読むべし

gcc を使う人は info gcc をくまなく読むべきだと言われたらそれでおしまいなのですが、info gcc の中で「Declaring Attributes of Functions」が面白かったので紹介します。
実は Monaカーネル内で GDT(Global Descriptor Table) 関連のコードが最適化オプション -O2 では動くけど -O3では動かないという現象に出くわしました。

/* TSS. Mona has only one TSS */
setSegDesc(&g_gdt[4], (dword)g_tss, 0x00000067, SEGMENT_PRESENT | SEGMENT_DPL0 | 0x00 | 0x09);

のように TSS を設定したあとに

/* prepare dpl0 stack */
memset(g_tss, 0, sizeof(TSS));
g_tss->esp0 = 0x90000;
g_tss->ss0  = KERNEL_SS;
/* load TSS */
ltr(selector);

と ltr するのですがコンパイラから見ると g_tss の指し示す内容と selector で指定されているセグメントが関連していることは知りようがななく間違った inline 最適化をしてしまっているのをTinoさんが突き止めてくれました。

全体的に inline 化を防ぐには -fno-inline とすれば良いのですがもっと良い方法がないかと info gcc していたら「Declaring Attributes of Functions」をみつけました。
それによれば、なんと inline 化して欲しくない関数には noinline attribute を指定してやれば良いのです。

static void ltr(word selector) __attribute__ ((noinline));


これでこの関数は絶対に inline 化されなくなります。


noinline の他にもいろいろと有用な attribute が存在します。
その中で Mona 的に使ったら良いのではないか?と思ったものをいくつか紹介します。
どれも、どういうことが起こりどのような効果があるか?が書いてあってとても勉強になります。
早速使えるものがあればぜひ使ってみましょう(nonull, malloc,noreturnなど)>shadowさん、Yumeさん

`always_inline'
`constructor'
`destructor'
     The `constructor' attribute causes the function to be called
     automatically before execution enters `main ()'.  Similarly, the
     `destructor' attribute causes the function to be called
     automatically after `main ()' has completed or `exit ()' has been
     called.  Functions with these attributes are useful for
     initializing data that will be used implicitly during the
     execution of the program.
`malloc'
     The `malloc' attribute is used to tell the compiler that a function
     may be treated as if any non-`NULL' pointer it returns cannot
     alias any other pointer valid when the function returns.  This
     will often improve optimization.  Standard functions with this
     property include `malloc' and `calloc'.  `realloc'-like functions
     have this property as long as the old pointer is never referred to
     (including comparing it to the new pointer) after the function
     returns a non-`NULL' value.
`noinline'
     This function attribute prevents a function from being considered
     for inlining.
`noreturn'
     A few standard library functions, such as `abort' and `exit',
     cannot return.  GCC knows this automatically.  Some programs define
     their own functions that never return.  You can declare them
     `noreturn' to tell the compiler this fact.  For example,

          void fatal () __attribute__ ((noreturn));

          void
          fatal (/* ... */)
          {
            /* ... */ /* Print error message. */ /* ... */
            exit (1);
          }

     The `noreturn' keyword tells the compiler to assume that `fatal'
     cannot return.  It can then optimize without regard to what would
     happen if `fatal' ever did return.  This makes slightly better
     code.  More importantly, it helps avoid spurious warnings of
     uninitialized variables.

     The `noreturn' keyword does not affect the exceptional path when
     that applies: a `noreturn'-marked function may still return to the
     caller by throwing an exception or calling `longjmp'.

     Do not assume that registers saved by the calling function are
     restored before calling the `noreturn' function.

     It does not make sense for a `noreturn' function to have a return
     type other than `void'.
`nothrow'
     The `nothrow' attribute is used to inform the compiler that a
     function cannot throw an exception.  For example, most functions in
     the standard C library can be guaranteed not to throw an exception
     with the notable exceptions of `qsort' and `bsearch' that take
     function pointer arguments.  The `nothrow' attribute is not
     implemented in GCC versions earlier than 3.3.
`warn_unused_result'
     The `warn_unused_result' attribute causes a warning to be emitted
     if a caller of the function with this attribute does not use its
     return value.  This is useful for functions where not checking the
     result is either a security problem or always a bug, such as
     `realloc'.

          int fn () __attribute__ ((warn_unused_result));
          int foo ()
          {
            if (fn () < 0) return -1;
            fn ();
            return 0;
          }

     results in warning on line 5.
`nonnull (ARG-INDEX, ...)'
     The `nonnull' attribute specifies that some function parameters
     should be non-null pointers.  For instance, the declaration:

          extern void *
          my_memcpy (void *dest, const void *src, size_t len)
            __attribute__((nonnull (1, 2)));

     causes the compiler to check that, in calls to `my_memcpy',
     arguments DEST and SRC are non-null.  If the compiler determines
     that a null pointer is passed in an argument slot marked as
     non-null, and the `-Wnonnull' option is enabled, a warning is
     issued.  The compiler may also choose to make optimizations based
     on the knowledge that certain function arguments will not be null.