きつねさんでもわかるLLVM読書会amagasaki.rb第5章
-
Upload
takayuki-kurosawa -
Category
Technology
-
view
720 -
download
3
description
Transcript of きつねさんでもわかるLLVM読書会amagasaki.rb第5章
きつねさんでもわかるLLVM読書会 第2回
7/5tkuro11
13年7月6日土曜日
自己紹介
•最近(悪い意味で)世間をにぎわす 某半導体会社社員。
•実はエンジニアじゃない
•おてやわらかに。。。
tkuro11
13年7月6日土曜日
祝単行本化
13年7月6日土曜日
きつねさんでもわかるLLVMの訂正一覧
貢献してます!
13年7月6日土曜日
1ゲトー⊂(゚Д゚⊂⌒`つ≡≡≡ズザーーーーーッ
きつねさんでもわかるLLVMの訂正一覧
貢献してます!
13年7月6日土曜日
さて......
13年7月6日土曜日
フロントエンドとは
バッグエンドA
フロントエンド
C
バッグエンドB
フロントエンド
Ruby
A向けコード
B向けコード
Cソース
rubyソース
言語依存
中間表現
ターゲット依存
13年7月6日土曜日
フロントエンドとは
• 普通はソースコードから中間表現まで担当
• LLVMの場合 、バックエンドがLLVM-IR
をいじり回すのでIRコード生成が中間表現
• 字句解析、構文解析、意味解析、コード生成
13年7月6日土曜日
フロントエンドとは
• 普通はソースコードから中間表現まで担当
• LLVMの場合 、バックエンドがLLVM-IR
をいじり回すのでIRコード生成が中間表現
• 字句解析、構文解析、意味解析、コード生成LLVMっぽくなるのはここから
13年7月6日土曜日
DummyCCompiler
• Cのサブセット
• 全て int 型、他の型無
• グローバル変数無
• 変数の宣言時初期化無
• 変数のカンマ区切りの宣言無
• includeとかdefine無
• ビット演算無
• 単項演算無
• 配列のサポート無
https://github.com/Kmotiko/DummyCCompiler.git
13年7月6日土曜日
DummyCどんなん?i n t t e s t ( i n t j ) {
i n t i ;
i = j * 1 0 ;
r e t u r n i ;
}
i n t m a i n ( ) {
i n t i ;
i = 1 0 ;
t e s t ( i ) ;
r e t u r n 0 ;
}
わー貧弱
13年7月6日土曜日
DummyCどんなん?i n t t e s t ( i n t j ) {
i n t i ;
i = j * 1 0 ;
r e t u r n i ;
}
i n t m a i n ( ) {
i n t i ;
i = 1 0 ;
t e s t ( i ) ;
r e t u r n 0 ;
}
わー貧弱
13年7月6日土曜日
フロントエンドの流れ
字句解析Lexer
構文解析Parser
意味解析SemanticAnalysis
最適化Optimizer
コード生成Code
Generation
ソースコード
13年7月6日土曜日
234
字句解析
• 入力ソースコード → トークンの列
• まずは入れ物を用意
• Tokenクラス
• TokenStreamクラス
i n tu
= 23 int ruby =4 ;r
yb
13年7月6日土曜日
Tokenクラス• 保持すべきもの
• 種類
• 文字列
• 数値
• 出現行数
列挙型のtypeで判別。継承にしてないのはコードふくれるから?
13年7月6日土曜日
TokenStreamクラス
• Token追加
• Token取得
• カレント巻き戻し
• カレント位置変更
• カレントの種類get
• カレントの文字列get
• カレントの数値get
• Token出力(util)
13年7月6日土曜日
TokenStreamクラスbool applyTokenIndex(int index){CurIndex=index;return true;}
微っっ妙・・・
bool getNextToken ();さらに微妙・・・ (ぶ、bool??????????)
13年7月6日土曜日
字句解析 実装• 方針としては
• ifstreamで一行ずつread
• 一文字ずつ読み込みトークン切り出し
• Tokenインスタンス生成 -> TokenStreamに
• 最後まで行ったらTokenStreamをreturn
13年7月6日土曜日
字句解析 実装
• いきなりですがバグってます。
• コメントごときで死亡•まあそれ以外は普通
• しかし++とかしすぎ(あちこちに状態変化)
13年7月6日土曜日
構文解析
• Tokenから文法に従って AST (Abstract Syntax Tree)に。
• つまりはグループ化して後続段で使いやすく
234int ruby =234ruby
=
int int
13年7月6日土曜日
文法の定義非終端記号 終端記号
13年7月6日土曜日
パーサの種類• トップダウン(LL)
• ボトムアップ(LR)
こっからスタート
CALL RET
CALL CALL
CALL
RET RET
RET
スタックに記号と状態を積んでいく
解析表に従い右(R)がマッチングしたらreduceする(ポンっと消えてトップに近づく)
E1 +2 E
13年7月6日土曜日
LR
13年7月6日土曜日
LR
13年7月6日土曜日
LR(k) LR(1)
構文解析
LALR(1)
LL(k)
SLR
LR(0)
LL(1)
LL(0)
パーサの種類
13年7月6日土曜日
LLの弱点
無限ループでござる
13年7月6日土曜日
isa<>とdyn_cast<>
• ちょっとC++な内容
• C++の(つかいにくーい)RTTIの代わりに
• isa<クラス>(instance)
• dyn_cast<to class>(from class)
• ベースclass -> 派生クラス変換(チェックつき)
13年7月6日土曜日
AST
• 基底クラスを作る (BaseAST)
• とみせかけて、なぜか列挙値によるIDフィールドつき(isaあるのに・・・)
13年7月6日土曜日
各ASTについて
• リテラル
• VariableAST, NumberAST
• 二項演算子
• asignment_expression, additive_expression, multiplicative_expression= + *
13年7月6日土曜日
各ASTについて• 変数宣言とジャンプ命令(return)
• VariableDeclAST, JumpStmtAST
• 関数とモジュール
• FunctionAST, TranslationUnitAS
• プロトタイプ宣言
• PrototypeAST
veryトップ
13年7月6日土曜日
VariableDeclAST注意点
関数の引数は同じローカル変数でも初期値があるため区別したかったらしい。普通の変数もゼロ初期化にすりゃ良いのに
13年7月6日土曜日
構文解析クラスの実装
• 戦略としては
• ファイル名→ LexicalAnalysis() →TokenStream
• 非終端記号ごとにfunctionを用意する
• いわゆる再起下降パーサ
• [コードを読む]
13年7月6日土曜日
例 assign & primary expr
13年7月6日土曜日
意味解析
• intしか無いので非常に簡単
• 実際、関数再定義の確認と、プロトタイプとのパラメータの数しか見ていない。
• パースと同時に解析を行う
13年7月6日土曜日
コード生成
• ASTまでOK
• ようやく...........!!!
13年7月6日土曜日
コード生成
• ASTまでOK
• ようやく...........!!!
LLVMたーいむ!!!13年7月6日土曜日
コード生成
Module作成
Function作成 >> Moduleへ
BasicBlock作成 >> Functionへ
Instruction生成 >> BasicBlock
繰り返し
最後に出力
13年7月6日土曜日
コード生成
• 本来ならGlobalVariableも挿入するがDummyCには Global変数がない(!そうでした)
• 該当するクラスを生成して作る
• IRBuilderというお手軽便利クラスがある
13年7月6日土曜日
IRBuilder
• llvm::IRBuilder(LLVMContext &C, MDNode *FPMathTag=0)
• コンテキストはllvm::getGlobalContextで
• テンプレート引数はいろいろ。よーわからん
13年7月6日土曜日
generateTranslationUnit
• Moduleの作成
• プロトタイプと関数定義生成メソッドを呼び出す
• プロトタイプと関数定義はASTのトップにvector<>として入っている
• [コード]
13年7月6日土曜日
generatePrototype
• prototypeASTから Function生成 -> Module
• ボディは生成しない
• Functionに必要となる型情報は llvm::Typeの関数から生成
• [コード]
13年7月6日土曜日
generateFunctionDefinition
• generatePrototypeが作ったFunctionに命令を入れるBasicBlockを追加
• BasicBlockはBasicBlock::Create()で生成
• その後、IRBuilderクラスのSetInsertPoint()で命令の挿入位置(カーソルみたいな物)を指定
• [コード]
13年7月6日土曜日
generateFunctionStatement
• 関数本体を生成
• FunctionStmtASTを辿り、変数定義、Statement定義を行う
• 実際に生成を行うのはこの下請けのgenerateVariableDeclarationとgenerateStatementになる
13年7月6日土曜日
generateVariableDeclaration
• BasicBlockに変数宣言を追加
• Alloca命令の生成 : IRBuilderの CreateAllocaでイケる
• (VariableASTの説明であった引数の初期)値の書き込みはCreateStoreで。
• [コード]
13年7月6日土曜日
generateStatement
• BaseASTの種類に応じてstatement生成
• 下請け関数に飛ばすだけ。
• isa<>のわかりやすい使用例
• ターンテーブル
• [コード]
13年7月6日土曜日
generateBinaryExpression
• 二項演算子コードの下請け
• 左辺/右辺値のコード生成を再起
• 代入または四則演算
• ここにまたバグが!(0.9.2では直ってます)
• [コード]
13年7月6日土曜日
generateCallExpression
• 関数呼び出し命令を生成
• IRBuilder::CreateCall
• 引数はvector<Value*>で与える
• nameは返り値の名前になる
• [コード]
13年7月6日土曜日
generateJmpStatement
• return命令を生成
• IRBuilder::CreateRet
• [コード]
だんだんなげやりに・・・・
13年7月6日土曜日
generateVariable
• 変数参照を実装 . Load命令を生成する
• IRBuilder::CreateLoad
• [コード]
だんだんやりなげに・・・・
13年7月6日土曜日
だんだんやりなげに・・・・
generateNumber
• 定数Valueを生成
• llvm::ConstantInt::get
• [コード]
13年7月6日土曜日
だんだんやりなげに・・・・
generateNumber
• 定数Valueを生成
• llvm::ConstantInt::get
• [コード]
13年7月6日土曜日
おつかれ様でした
• ありがとうございましたありがとうございました
13年7月6日土曜日
まだだ、まだ終わらんよ!!!13年7月6日土曜日
main関数について
• ドライバ部分が必要 ** gccみたいな
• 短いので全部mainに書いちゃえ、という思想らしい
13年7月6日土曜日
OptionParser
• お手製getopt()/boost::program_options
• なんでも自作好きだなー
• 例によって結構強引
• [コード]
13年7月6日土曜日
初期化とか • InitializeNativeTarget();
謎だったデータレイアウトはこれか!
• sys::PrintStackTraceOnErrorSignal()名前通り
• PrettyStackTraceProgram これも名前通り
• EnableDebugBuffering = true;
バッファのつじつまを合わせる?
13年7月6日土曜日
出力の仕方
• パスマネージャに llvm::createPrintModulePass を登録→
pm.run()するだけ。
• ファイルオブジェクトは raw_fd_ostreamを使う(これはエラー出力もパックしている)
• 結構簡単。
13年7月6日土曜日
初最適化(Passの指定)
• さっきもやったけど基本的に パスマネージャに pm.add() するだけ。
• mem2reg をやってみる(足すだけ)
• [コード]
13年7月6日土曜日
組み込み関数(printnum)
• どうせなので標準出力に出したい
• 方針は Clangで吐いた目的コードを出して
• Parserクラスにprintnum参照を追加
• リンクしてしまう
• 結構な力技
13年7月6日土曜日
JIT
• Just In Time
• llvm::ExcuteEngineを利用(JIT専用ではない)
• 実際にはサブクラスのllvm::Interpreter, llvm::JIT, llvm::MCJITのいずれかを利用
• MCはMachineCode 。in-memoryでよりアーキテクチャに近いコードのため、変換が速い
13年7月6日土曜日
ExecuteEngine
• llvm::EngineBuilder::EngineBuilder(Mod)
• そのあとcreate()メソッドを使用する
• createが返した ExecutionEngine*が実行環境の実体
• インクルードファイルで指定するか、またはExecusionEngine::setEngineKind()
• MCの場合は setUseMCJITでも行ける13年7月6日土曜日
JIT組み込み
• doCodeGenで作ったModuleを ExecutionEngineに食わせる
• ExecutionEngine::getPointerToFunctionにて実行アドレスを取得。関数呼び出しでOK
13年7月6日土曜日
Metadata
• Metadataには {MetadataString, MetadataNode}
• MetadataString → llvm::MDString
• MetadataNode → llvm::MDNode
• MDBuilderを利用する
13年7月6日土曜日
Metadata
• MDBuilderの生成メソッド
MDString *createString(StringRef Str) MDNode *createFPMath(float Accuracy) MDNode *createRange(const APInt &Lo, const APInt &Hi) MDNode *createAnonymousTBAARoot() MDNode *createTBAARoot(StringRef Name) MDNode *createTBAANode(StringRef Name, MDNode *Parent, bool isConstant = false) MDNode *createTBAAStructNode(ArrayRef<TBAAStructField> Fields) MDNode *createBranchWeights(uint32_t TrueWeight, uint32_t FalseWeight) MDNode *createBranchWeights(ArrayRef<uint32_t> Weights)
13年7月6日土曜日
MDBuilderの使い方
• 生成したMDBuilderインスタンスを使って MDB->createFPMath(0.5);
• のように使う
• まあそのまま
13年7月6日土曜日
Metadataの反映
• 方法1:llvm::Instruction::setMetadataにてInstructionインスタンスにMetadataをひも付け。 Stringか ID値にて登録できる
• 方法2:IRBuilder経由で演算にひも付け
13年7月6日土曜日
Metadata
• Metadataには {MetadataString, MetadataNode}
• MetadataString → llvm::MDString
• MetadataNode → llvm::MDNode
13年7月6日土曜日
CommandLine
• ごめんなさいココロが・・・・ココロが折れてしまいました。
13年7月6日土曜日
13年7月6日土曜日
まとめ
• フロントエンドの作成を説明
• 字句解析/構文解析/意味解析
• コード生成/JIT
• 長かった。。。
• もっとツール使えバカ
13年7月6日土曜日
ありがとうございましたほんとうにおつかれさまでした
13年7月6日土曜日