1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

SwiftでLLVM-Cを使う例

Last updated at Posted at 2023-08-03

はじめに

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

下準備

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

  • XcodeでCommand Line Toolのプロジェクトを作って、LLVMのヘッダとライブラリをサーチパスに追加。TARGETS - Build Settings - BasicHeader Search PathsLibrary Search Paths
    パスの確認はllvm-configでできる。

$ llvm-config --includedir
/opt/homebrew/Cellar/llvm/16.0.6/include
$ llvm-config --libdir
/opt/homebrew/Cellar/llvm/16.0.6/lib
  • TARGETS - Build PhasesLink Binary With LibrarieslibLLVM-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 - BasicObjective-C Bridging HeaderBridging-Header.hを追加。
    中身は今回使用する関数が含まれているものだけ。

#ifndef Bridging_Header_h
#define Bridging_Header_h

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

#endif /* Bridging_Header_h */

実装

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

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()

実行結果

特に面白いことは何もしていないけど、感じはわかった。

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でどうにか使えないか調べてみたい。

参考にしたサイト

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?