LLVM with mosh の0

skySchemeの開発ベースをmoshに移すことにしたのでLLVMを呼べるようにする。LLVMC++で書かれているのでちゃんとした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?を満たすのでポインタとリテラル整数を区別する手段が無い。
  • moshFFIはポインタの確保やポインタの配列を欠いているのでこれらは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)
      ))
  )