発端
LLVM bitcode はポータブルでない
http://shinh.skr.jp/slide/elvm/002.html
これがどういう意味なのか分からなくてずっと気になってました。
LLVMのbitcodeってターゲット非依存なんじゃないの?みたいな感じで。
ここでは「ポータブルでない」というのを
(実行される)プラットフォーム
または(コンパイルの)ターゲット
に
依存している(dependent)
または固有のもの(specific)を含む
こと
ぐらいにふんわり理解してます。
「だから〇〇ができない」みたいなことは考えません。
結論あるいは感想
- LLVM IRとそれをまとめたbitcode自体はターゲットに依存しない
- 実際にはだいたい(?)ターゲットに依存したIRが出力される
- C系の場合にはほぼ不可避
- Swiftでもターゲット依存のIRっぽい
- low levelな中間表現なんだからそりゃターゲット依存にもなるでしょ
- 正しいのかは分からないけどそういう気持ちになった
- ただしLLVMは何の略でもないことになってる
C/C++/Objective-C
Cとその上位互換たちの話のはずなので、Objective-Cもたぶん該当する
LLVM公式のFAQを翻訳してちょっとコメント
Question
Can I compile C or C++ code to platform-independent LLVM bitcode?
CやC++のコードをプラットフォームに依存しないLLVM bitcodeにコンパイルできますか?
Answer
3分割してタイトルを付けます。
コンパイル前(プリプロセッサ)
No. C and C++ are inherently platform-dependent languages. The most obvious example of this is the preprocessor. A very common way that C code is made portable is by using the preprocessor to include platform-specific code. In practice, information about other platforms is lost after preprocessing, so the result is inherently dependent on the platform that the preprocessing was targeting.
できません。CやC++は本質的にプラットフォーム依存な言語です。いちばん分かりやすい例としては、プリプロセッサがあります。Cのコードをポータブルにするためによくあるやり方は、プリプロセッサを使ってプラットフォーム固有のコードを含めることです。実際のところ、他のプラットフォームの情報はプリプロセスの後では失われ、その成果物はプリプロセスが対象としたプラットフォームに本質的に依存したものとなります。
要するに、LLVM IRを作るコンパイル以前に、既にプラットフォーム依存なコードになってるのが普通というわけです。
コンパイル時(sizeof)
Another example is sizeof. It’s common for sizeof(long) to vary between platforms. In most C front-ends, sizeof is expanded to a constant immediately, thus hard-wiring a platform-specific detail.
もう1つの例として、sizeofがあります。よく知られているように、sizeof(long)はプラットフォームごとに変わります。多くのC言語のフロントエンドでは、sizeofを定数値に直接展開して、プラットフォーム固有の詳細を埋め込みます。
ここでのフロントエンドは、clangとか、LLVMのフロントエンドのことですね。プリプロセッサのようにコンパイル前だけでなく、コンパイル過程でもプラットフォーム固有の値がそのまま入ってしまうのが普通、ということでしょう。
ABI
Also, since many platforms define their ABIs in terms of C, and since LLVM is lower-level than C, front-ends currently must emit platform-specific IR in order to have the result conform to the platform ABI.
また、多くのプラットフォームでABIはCレベルで定義され、そしてLLVMはCより低レベルなので、そのプラットフォームのABIに合わせた成果物を得るために、フロントエンドはプラットフォーム固有のIRを出力しなければならないのが現状です。
私はABIというものをちゃんと分かっていません。なので、それっぽい翻訳だけにとどめておきます。
Swift
SILのドキュメントでのLLVM IRの扱い
SILというのは、コンパイル過程でASTとLLVM IRの中間にあるやつで、raw SILとcanonical SILの2段階があります。それ以外のことは何も分かってませんが、LLVM IRとの対比で興味深い表現を見つけました。
In contrast to LLVM IR, SIL is a generally target-independent format representation that can be used for code distribution, but it can also express target-specific concepts as well as LLVM can.
LLVM IRとは対照的に、SILは専らターゲットに依存しないフォーマットの表現形態で、コード配布に使うことができます。しかし、LLVMと同様に、ターゲット特有の概念を表現することもできます。
ここではLLVM IRがターゲット依存なものとして扱われているように読めます。これがC系のように不可避の現実なのか、(例えば何らかの最適化のための?)選択なのかは分かっていませんが、high-levelなSILではターゲットに依存しない表現を扱い、ターゲット依存なものはlow levelなLLVM IRの担当になっているようです。
Rust
LLVMのフロントエンドとして、C系、Swiftの他にRustでの扱いも挙げておきます。なお、私はRustのことは何も知りません。
Rustコミュニティでの質問と回答
-
Cross Platform: Rust to LLVM IR and is LLVM IR is portable?
- 私の気になってた話と回答がそのままここに
- Rust特有の話っぽいのはあんまりちゃんと読んでません
- LLVM公式のFAQへのリンクもあります
TODO
MIRの記事から何か分かるといいな
-
Introducing MIR
- SwiftのSILのように、LLVM IRの手前に別の中間表現があるようです。
- ほぼ図しか見てないので、そのうち読んで何か追記します。