[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.