HN🔥 132
💬 47

C言語拡張の移植性と代替コンパイラの活用術

xngbuilds
約23時間前

ディスカッション (11件)

1
whizzter
約23時間前

C言語のプロジェクトで個人的に一番イラッとするのが、Linuxユーザーが書くと大抵「自分の環境では動く」程度のレベルで止まっちゃうことなんだよね(WindowsやFreeBSDユーザーだと、その両方のプラットフォームでハマることになるし)。記事でも典型的な例が挙げられてる。

  #if !(defined __GNUC__ || defined __clang__ || defined __TINYC__)
  # define __attribute__(xyz)     /* Ignore */
  #endif

この !defined チェックの中に、attribute がすでに定義されているかどうかのチェックを入れない理由なんてないはずなんだけど(自作コンパイラの作者なら、デフォルトで内部の mycompiler__attribute に変換されるような attribute の define を強制できるだろうし)。

それに、FreeBSDでコンパイルしようとすると、systemdの依存関係とかPOSIX準拠じゃない挙動によくぶち当たる(Windowsの話はするまでもないけど、ここで論争したくないから控えておくよ)。

2
gritzko
約22時間前

コンパイラやOSの行列(マトリックス)に対して、現実的にコードをテストするいい方法ってあるの?

3
WalterBright
約22時間前

あるよ。ImportC(D言語のコンパイラに組み込まれたCコンパイラ)を実装したとき、様々な .h ファイルに入り混じるめちゃくちゃな独自仕様の対応に追われてかなりの時間を費やしたね。

https://github.com/dlang/dmd/blob/master/druntime/src/import... (https://github.com/dlang/dmd/blob/master/druntime/src/importc.h)

https://github.com/dlang/dmd/blob/master/druntime/src/__impo... (https://github.com/dlang/dmd/blob/master/druntime/src/__importc_builtins.di)

4
fuhsnn
約21時間前

GCCやClangのふりをするつもりはないけれど、実用的なプロジェクトをコンパイルしたいというインディー系Cコンパイラ開発者なら、slimccのテストスクリプト[1]やプラットフォームヘッダーのハック[2]が役に立つかもね。
[1] https://github.com/fuhsnn/slimcc/blob/main/scripts/linux_thi... (https://github.com/fuhsnn/slimcc/blob/main/scripts/linux_thirdparty.bash)

[2] https://github.com/fuhsnn/slimcc/blob/main/slimcc_headers/pl... (https://github.com/fuhsnn/slimcc/blob/main/slimcc_headers/platform_fix/)

他にも面白い話があるよ:

  • ゲームプロジェクトはSIMDをデフォルトで使うから、例えばSDLやSTBでは常に -DSDL_DISABLE_IMMINTRIN_H と -DSTB_NO_SIMD を渡す必要がある
  • math.h の NAN は通常 (0.0f / 0.0f) にフォールバックするんだけど、printf で「-nan」と出力されるせいで、「nan」を期待しているテストスイートが落ちるプロジェクトがある
  • NetBSDの sys/cdefs.h は、GCCかPCCのふりをしないと容赦なく #error を吐く
  • いくつかのプロジェクトは、非static関数に対して attribute((always_inline)) を使っているから、これがないとコンパイルできない
  • 多くのプロジェクトはビルドシステムで -fvisibility を調べて -fvisibility=hidden を渡すのに、ヘッダー内では attribute((visibility(default))) を GNUC チェックで囲っているから、シンボル欠落が起きる
  • static_assert() の代用として if(0) { undefined_function() } を使うプロジェクトがある。QEMUからClangへのバグ報告もあったよ。-O0 でこの書き方の if が最適化されないのが原因だった
  • STDC_NO_VLA を定義しても、テストされてなくてバグだらけの alloca() コードパスにフォールバックされることがある(Pythonとjemallocの両方でこの問題があって報告済み)
  • Valkey は __builtin_ctzll のフォールバックが壊れてた(報告済み)
  • ZigのCブートストラップパスは、コンパイラがGCC/Clangレベルの最適化を備えていると期待しているため、そうじゃないとスタックオーバーフローする(報告済み)
  • Rubyをslimccでコンパイルするために stdatomic.h のコードパスを寄稿したけど、たぶんそのコードパスを使ってるのは今でもうちだけだろうね
  • gnu_asm をクエリするために __has_extension を実装したんだけど、SQLiteが __has_extension(c_atomic) をGNUアトミック組み込み関数を調べるために使っていて壊れた。c_atomic は本来C11の _Atomic のためのものなのに(個人的には __has_builtin を使うべきだと思う)
5
meghprkh
約21時間前

なぜこんな風にしないんだろう?

  + #if !(defined __GLIBC_COMPILER_SUPPORTS_ATTRIBUTES__)
  - #if !(defined __GNUC__ || defined __clang__ || defined __TINYC__)
  # define __attribute__(xyz)     /* Ignore */
  #endif

(あるいは、使おうとする属性ごとに細かく分けるとかね)

コンパイラに基づくダウンストリームのC++ライブラリでは、OSやコンパイラのプラットフォームをチェックするのはかなり一般的なことだし(例えば Boost.Config)。モダンC++だと、ある程度標準化も進んでるよ https://en.cppreference.com/cpp/utility/feature_test

6
rurban
約21時間前

tccに対抗するために高速な小型コンパイラ rcc を実装したけど、glibcヘッダーの奇妙な挙動はClangと同じ方法で簡単に直せたよ。GCCのプリデファインをすべて定義して、GCCの拡張機能をすべて実装するだけ。1日でできた。それに、うちのヘッダーはglibcと比べてクリーンだよ。このカテゴリの他のコンパイラには slimcc、kefir、cproc がある。glibcのヘッダーをパースできるコンパイラは他にないよ。tccはその点、特別に例外対応してるんだ。

7
einpoklum
約19時間前

Cを書いたことがある人なら誰でも、ISO C標準に完全に準拠したコードなんて非現実的でレアだってことを知っている。

それなりの量のCを書いてきたけど、それがレアだとは思わないな。特にアプリケーションよりもライブラリの場合、そしてプロプライエタリなコードよりもFOSSの場合ならなおさら。

世の中の実用的なCコードのほとんどは、多かれ少なかれ非標準の挙動や言語拡張に依存している

どの分野で働いているかにもよるんじゃないかな。PCをターゲットにしていない企業だと、独自の挙動や拡張に依存するのは難しいんだ。ターゲットデバイスのコンパイラがGCCなどの便利な機能をサポートしていない可能性があるから、安全策をとってC99(ひどい時にはC89)にとどまることになる。それでも標準にはあるけど……まあ、それが十分に堅牢にサポートされていると信頼するのは警戒するかな。例えば可変長配列(VLA)みたいにね。

もちろん、コードが単一のマシンだけをターゲットにしないとなると、移植性や標準準拠なんかを心配せざるを得なくなるんだけどね。

8
cocodill
約19時間前

コンパイラのレベルでオープンソースの移植性について文句を言うなら、オープンソースの精神を吸い込んで口を閉じてろ。そして息を吐いて、自分でその魔法のニッチなコンパイラに移植すればいいだろ。

9
kscarlet
約15時間前

Common Lispのエコシステムは、多数の実装がどうやって一緒に前進していくかという良い模範になっていると思う。実装が実験的に拡張を取り入れて、本当に便利なものは複数の実装で採用されて、十分に良ければ移植性ライブラリが事実上の標準として現れる。その成果は https://portability.cl/ で見られるよ。標準化委員会が40年近く前に解散したのに、言語は今まで以上に進化してるんだ!

10
ndesaulniers
約14時間前

例えばClangでLinuxカーネルをコンパイルできるようにするには、両方のプロジェクトにパッチが必要で、かなりの労力を要した。

よくわかるよ。:-)