LLVM with mosh の0
skySchemeの開発ベースをmoshに移すことにしたのでLLVMを呼べるようにする。LLVMはC++で書かれているのでちゃんとしたbindingを書くのが望ましいが、面倒なのでFFIを使う。原理上、FFIからはCインターフェースしか使うことは出来ない。
現状ではかなり再現性が低い。
- moshのLIBSにpthreadを追加する(LLVMのJITCがpthreadに依存している)。
- LLVMをmake SHARED_LIBRARY=1でmakeする(llvm-configのビルド時に失敗するが問題ない)。
- デバッグする可能性も考えると、./configure --disable-optimizeするほうが良い。
- x86_64でしか動作しない。long long int = intでなければならない。
- ポインタがinteger?を満たすのでポインタとリテラル整数を区別する手段が無い。
- moshのFFIはポインタの確保やポインタの配列を欠いているのでこれらはchar*として渡せるbytevectorで代用している
- LLVMの作るshared libraryは依存関係を自動的には解決しない。そのため、ロードする順番を手で調整する必要が有る。簡単にはllvm-config engine --libsの出力順の逆順にする。先に書いたように現状のLLVMはshared libraryをビルドするときにllvm-configの作成に失敗するので、通常のLLVMも作っておくのが望ましい。
ちょっとした解説
このプログラム(リストは最後)は、引数に1を加える関数を生成し、LLVMのJITCでコンパイルして動作させる。
oku@shie ~/mosh/skymosh/mosh-scheme-read-only $ ./mosh ../test.scm 0 hoge ; ModuleID = 'hoge' define i32 @main(i32) { function_entry: %add = add i32 %0, 1 ; <i32> [#uses=1] ret i32 %add } 4
最後の4が、生成した関数の結果となっている。
この関数の生成は、プログラム末尾のlet*内で行っている。
(let* ( (argptr (ptr-of INT32)) (functype (FunctionType INT32 argptr 1 0)) (func (AddFunction MyModule "main" functype)) (BB (AppendBB func "function_entry")) ) (SetFunctionCallConv func 0) (AtEnd BUILDER BB) ; プログラム本体 (return (add (GetParam func 0) (const 1))) ; LLVM中間コードの出力 (DumpModule MyModule) ;実行 (display (GenericValueToInt (RunFunction (unbox-cpointer EE) func 1 (ptr-of (GenericValueOfInt INT32 3 0) ) ) 0 )) (newline) ))
BasicBlockはC言語で言うところの{ }、アセンブラで言うところのラベルに相当する。
BUILDERはカーソルだと考えればよい。AtEndは作成したBasicBlockの最後にカーソルを位置づける。
以下全コード。
(import (rnrs) (mosh ffi)) (define (box-cpointer) (make-bytevector 8)) (define (ptr-of X) (let ((ptr (make-bytevector 8))) (bytevector-u64-native-set! ptr 0 X) ptr)) (define (unbox-cpointer X) (bytevector-u64-native-ref X 0)) (let* ( (llvmSys (open-shared-library "/home/oku/mosh/skymosh/llvmobj/Debug/lib/libLLVMSystem.so")) (llvmSup (open-shared-library "/home/oku/mosh/skymosh/llvmobj/Debug/lib/libLLVMSupport.so")) (llvmCore (open-shared-library "/home/oku/mosh/skymosh/llvmobj/Debug/lib/libLLVMCore.so")) (llvmTarget (open-shared-library "/home/oku/mosh/skymosh/llvmobj/Debug/lib/libLLVMTarget.so")) (llvmAn (open-shared-library "/home/oku/mosh/skymosh/llvmobj/Debug/lib/libLLVMAnalysis.so")) (llvmipa (open-shared-library "/home/oku/mosh/skymosh/llvmobj/Debug/lib/libLLVMipa.so")) (llvmTU (open-shared-library "/home/oku/mosh/skymosh/llvmobj/Debug/lib/libLLVMTransformUtils.so")) (llvmSO (open-shared-library "/home/oku/mosh/skymosh/llvmobj/Debug/lib/libLLVMScalarOpts.so")) (llvmCG (open-shared-library "/home/oku/mosh/skymosh/llvmobj/Debug/lib/libLLVMCodeGen.so")) (llvmEE (open-shared-library "/home/oku/mosh/skymosh/llvmobj/Debug/lib/libLLVMExecutionEngine.so")) (llvmJIT (open-shared-library "/home/oku/mosh/skymosh/llvmobj/Debug/lib/libLLVMJIT.so")) (llvmDAG (open-shared-library "/home/oku/mosh/skymosh/llvmobj/Debug/lib/libLLVMSelectionDAG.so")) (llvmx86 (open-shared-library "/home/oku/mosh/skymosh/llvmobj/Debug/lib/libLLVMX86CodeGen.so")) (llvmAsmP (open-shared-library "/home/oku/mosh/skymosh/llvmobj/Debug/lib/libLLVMAsmPrinter.so")) (llvmx86a (open-shared-library "/home/oku/mosh/skymosh/llvmobj/Debug/lib/libLLVMX86AsmPrinter.so")) (CreateExecutionEngine (c-function llvmEE int LLVMCreateExecutionEngine char* void* char*)) (CreateModuleProviderForExistingModule (c-function llvmCore void* LLVMCreateModuleProviderForExistingModule void*)) (CreateBuilder (c-function llvmCore void* LLVMCreateBuilder)) ; (void) (INT32 ((c-function llvmCore void* LLVMInt32Type) )) ; (void) (Build+ (c-function llvmCore void* LLVMBuildAdd void* void* void* char*)) (BuildRet (c-function llvmCore void* LLVMBuildRet void* void*)) (Const32Build (c-function llvmCore void* LLVMConstInt void* int int)) (CreateModule (c-function llvmCore void* LLVMModuleCreateWithName char*)) (AddFunction (c-function llvmCore void* LLVMAddFunction void* char* void*)) (AppendBB (c-function llvmCore void* LLVMAppendBasicBlock void* char*)) (DumpModule (c-function llvmCore void LLVMDumpModule void*)) (FunctionType (c-function llvmCore void* LLVMFunctionType void* char* int int)) (AtEnd (c-function llvmCore void LLVMPositionBuilderAtEnd void* void*)) (GetParam (c-function llvmCore void* LLVMGetParam void* int)) (SetFunctionCallConv (c-function llvmCore void* LLVMSetFunctionCallConv void* int)) (RunFunction (c-function llvmEE void* LLVMRunFunction void* void* int char*)) (GenericValueToInt (c-function llvmEE int LLVMGenericValueToInt void* int)) (GenericValueOfInt (c-function llvmEE void* LLVMCreateGenericValueOfInt void* int int)) ) (let* ( (EE (box-cpointer)) (LLVMError (box-cpointer)) (MyModule (CreateModule "hoge")) (BUILDER (CreateBuilder)) ) (define (const N) (Const32Build INT32 N 0)) (define (add A B) (Build+ BUILDER A B "add")) (define (return X) (BuildRet BUILDER X)) (display (CreateExecutionEngine EE (CreateModuleProviderForExistingModule MyModule) LLVMError)) (newline) (display (pointer->string (unbox-cpointer LLVMError))) (newline) (let* ( (argptr (ptr-of INT32)) (functype (FunctionType INT32 argptr 1 0)) (func (AddFunction MyModule "main" functype)) (BB (AppendBB func "function_entry")) ) (SetFunctionCallConv func 0) (AtEnd BUILDER BB) (return (add (GetParam func 0) (const 1))) (DumpModule MyModule) (display (GenericValueToInt (RunFunction (unbox-cpointer EE) func 1 (ptr-of (GenericValueOfInt INT32 3 0) ) ) 0 )) (newline) )) )