SwiftでLLVM-Cを使う例

## はじめに
Xcodeの次のバージョンではC++との連携が便利になるらしいので、今のうちにライブラリ等を使わずにLLVM-Cを直に使ってLLVMの感じを調べた。

### 下準備

* Homebrew等でLLVMの最新版(執筆時点では16.0.6)をインストール。

* XcodeでCommand Line Toolのプロジェクトを作って、LLVMのヘッダとライブラリをサーチパスに追加。`TARGETS` - `Build Settings` - `Basic`の`Header Search Paths`と`Library Search Paths`。
パスの確認は`llvm-config`でできる。
```sh
$ llvm-config --includedir
/opt/homebrew/Cellar/llvm/16.0.6/include
$ llvm-config --libdir
/opt/homebrew/Cellar/llvm/16.0.6/lib
```

* `TARGETS` - `Build Phases`の`Link Binary With Libraries`に`libLLVM-C.dylib`を追加。
このままだと`libLLVM-C.dylib`を見つけてくれないので、`TARGETS` - `Signing & Capabilities`タブの`Hardened Runtime`の中の`Disable Library Validation`にチェックを入れる。

* Swiftから関数が使えるようにするための、`Bridging-Header.h`を作る。(File - New - File...からHeader Fileを選ぶ)
`TARGETS` - `Build Settings` - `Basic`の`Objective-C Bridging Header`に`Bridging-Header.h`を追加。
中身は今回使用する関数が含まれているものだけ。
```c
#ifndef Bridging_Header_h
#define Bridging_Header_h

#include <llvm-c/Core.h>
#include <llvm-c/ExecutionEngine.h>

#endif /* Bridging_Header_h */
```

## 実装
参考にしたサイトとほとんど何にも変わらない。
できるだけ記述をコンパクトにまとめて、LLVMContexRefを使用するようにしただけ。

```swift
func llvm() {
    let ctx = LLVMContextCreate()
    defer { LLVMContextDispose(ctx) }
    let mod = LLVMModuleCreateWithNameInContext("Hello", ctx)
    defer { LLVMDisposeModule(mod) }
    let i32 = LLVMInt32TypeInContext(ctx)

    // sum: int(*)(int, int)
    var spt = [i32, i32]
    let sft = LLVMFunctionType(i32, &spt, UInt32(spt.count), LLVMBool(0))
    let sum = LLVMAddFunction(mod, "sum", sft)
    let sbb = LLVMAppendBasicBlockInContext(ctx, sum, "entry")
    let sb = LLVMCreateBuilderInContext(ctx)
    defer { LLVMDisposeBuilder(sb) }
    LLVMPositionBuilderAtEnd(sb, sbb)
    LLVMBuildRet(sb, LLVMBuildAdd(sb, LLVMGetParam(sum, 0), LLVMGetParam(sum, 1), "add_tmp"))

    // main: int(*)(void)
    let mft = LLVMFunctionType(i32, nil, 0, LLVMBool(0))
    let wrap = LLVMAddFunction(mod, "__wrapper_main", mft)
    let mbb = LLVMAppendBasicBlockInContext(ctx, wrap, "entry")
    let mb = LLVMCreateBuilderInContext(ctx)
    defer { LLVMDisposeBuilder(mb) }
    LLVMPositionBuilderAtEnd(mb, mbb)
    let x = 10, y = 25
    var args = [LLVMConstInt(i32, UInt64(x), LLVMBool(0)),
                LLVMConstInt(i32, UInt64(y), LLVMBool(0)), ]
    LLVMBuildRet(mb, LLVMBuildCall2(mb, sft, sum, &args, UInt32(args.count), "call_tmp"))

    // execution
    LLVMLinkInMCJIT()
    LLVMInitializeNativeTarget()
    LLVMInitializeNativeAsmPrinter()
    var ee: LLVMExecutionEngineRef? = nil
    var err: UnsafeMutablePointer<CChar>? = nil
    defer { err?.deallocate() }
    LLVMLinkInInterpreter()

    guard LLVMCreateExecutionEngineForModule(&ee, mod, &err) == 0 else {
        print(String(cString: err!))
        LLVMDisposeMessage(err)
        return
    }

    let z = LLVMRunFunction(ee, wrap, 0, nil)
    print("\(x) + \(y) = \(LLVMGenericValueToInt(z, LLVMBool(0)))")

    LLVMDumpModule(mod)
}
llvm()
```
### 実行結果
特に面白いことは何もしていないけど、感じはわかった。
```LLVM
10 + 25 = 35
; ModuleID = 'Hello'
source_filename = "Hello"
target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"

define i32 @sum(i32 %0, i32 %1) {
entry:
  %add_tmp = add i32 %0, %1
  ret i32 %add_tmp
}

define i32 @__wrapper_main() {
entry:
  %call_tmp = call i32 @sum(i32 10, i32 25)
  ret i32 %call_tmp
}
```

## おわりに
Xcodeのバージョンが15になったらC++で書き直してみたい。
また、ORCというJITもあるみたいなので、LLVM-Cでどうにか使えないか調べてみたい。

## 参考にしたサイト
https://lowlevelbits.org/how-to-use-llvm-api-with-swift.-addendum/

https://www.pauladamsmith.com/blog/2015/01/how-to-get-started-with-llvm-c-api.html