Sunday, 4 December 2011

LLVM ソースの続き

ソースをわざと追跡できないようにする難読化なんていう技術もあるんですけどね。

最近読んだ Linux Kernel と LLVM は、そういう難読化が入っているわけではないのだが読みにくい。共通しているのは、
<pre>
Preprocessor の文字結合を作ったコード生成
</pre>

かな。もちろん、gcc -E で Preprocessor 処理後のコードを見れば良いわけなのだが、

<pre>
 定義されている部分をgrepやcode referencerで追跡できない
</pre>

ってのは厳しい。script で成形する手法の方が良いと思うのだが Windows だと言語に閉じる方が好まれるんだろうな。

あとは、LLVMのポインタタグ。古い技術だと思うし欠点も結構議論されていたと思うんだよね。おかげで C++ の意味不明 cast 連発。gdb で trace する時に cast を何重にも追跡する羽目になる。見かけを良くするためにだけ階層を深くするというあれですね。

利点としてはメモリを食わないのとポインタの先を見に行かなくても条件判断できるってのがあるんだけど、CPUやキャッシュの予測を下げてしまうし、ソースは読みづらいし。いや、まぁ、CPUが「ポインタのアライメント分は無視する」ってな構造だったらよいのかもしれないけどさ。今でも「アドレスの上位は使わないからタグに使う」とかのセコイ技多い。

Parse/ParseExpr.cpp の中で定義されているクラス名が Parser::ParseExpression ってのは、なんかの冗談ですかね。

あと、これ

<pre>
llvm::Value *&DMEntry = LocalDeclMap[&D];
  DMEntry = GV;
</pre>

かな。LocalDeclMap は Hash 表で [] の中はキーなんだだよね。&D というアドレスがキーというわけ。しかも、それを *& で受けるから、

<pre>
  DMEntry = GV;
</pre>

これは、DMEntry を変更しているのではなくて、 Hash 表の中に GV をコピーしているわけだ。なんで、

<pre>
LocalDeclMap->put(D);
</pre>

じゃあかんのか?

これ見てもそうなんだけど、変数は引数を含めて基本的に大文字。小文字はmodule名にしか使わないらしい。Windows っぽいかな。Unix 流ではないね。

namespace を明示するルールらしい。
<pre>
if (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected_property_name);
return ExprError();
}
</pre>
みたいな。長い名前は苦にならないらしい。IDEは何を使っているんだろう? Emacs?

コメントは「バグの言い訳」で機能のコメントはしないという方針らしいです。標準もないみたい。/**/ は使わないのは良いかな。FIXME. が多い。

複雑な条件も問題ありません。
<pre>
// Support 'Class.property' and 'super.property' notation.
if (getLang().ObjC1 && Tok.is(tok::period) &&
(Actions.getTypeName(II, ILoc, getCurScope()) ||
// Allow the base to be 'super' if in an objc-method.
(&II == Ident_super && getCurScope()->isInObjcMethodScope()))) {
ConsumeToken();
</pre>
なんかため息出ちゃうよ… コンパイラのコードなんてこんなものだけどね。

class でなく struct を使って、そこにメソッドを書くってのも LLVM の流行らしい。

<pre>
struct DestroyNRVOVariable : EHScopeStack::Cleanup {
DestroyNRVOVariable(llvm::Value *addr,
const CXXDestructorDecl *Dtor,
llvm::Value *NRVOFlag)
: Dtor(Dtor), NRVOFlag(NRVOFlag), Loc(addr) {}

const CXXDestructorDecl *Dtor;
llvm::Value *NRVOFlag;
llvm::Value *Loc;

void Emit(CodeGenFunction &CGF, Flags flags) {
// Along the exceptions path we always execute the dtor.
</pre>

assert が多い。落ちたらどーすんだ。Release時にassert落とすななんていう説もあるのは知っているけど。

まぁ、いろいろ楽しめるコードだな。動いているコードは汚いものだが。それでも、今まで見た C++ では、まともな方だと思う。Iterator が多く使われてます。

<pre>
for (DeclStmt::decl_iterator I = DS->decl_begin(), E = DS->decl_end();
I != E; ++I) {
</pre>

clang には最適化は入ってないので、ある意味で読みやすい。今回は、clang しか読めませんでしたが、続きはいつかな〜

Post a Comment