歡迎加入QQ討論群258996829
贏了現(xiàn)在輸了未來 頭像
蘋果2袋
2
贏了現(xiàn)在輸了未來

通過 LLVM 在 Android 上運(yùn)行 Swift 代碼

發(fā)布時間:2015-10-15 19:10  回復(fù):0  查看:4374   最后回復(fù):2015-10-15 19:10  

Swift 已經(jīng)發(fā)布一年多了,蘋果承諾將在 2015 年底開源 Swift。這是非常棒的一件事情,但是我們現(xiàn)在可以在 Android 設(shè)備上運(yùn)行 Swift 嗎?

Swift 編譯器

這都是由 Chris Lattner 設(shè)計(jì)的,很容易就可以發(fā)現(xiàn) Swift 的編譯器是基于 LLVM 構(gòu)建的。LLVM 是個編譯器基礎(chǔ)設(shè)施,利用了了一個可重定向編譯器的有趣概念。

通過 LLVM 在 Android 上運(yùn)行 Swift 代碼

也就是說,不是生成特定架構(gòu)的機(jī)器代碼,LLVM 為一個虛擬機(jī)生成匯編代碼,然后轉(zhuǎn)換成中間代碼,適配架構(gòu)需要的實(shí)際代碼。

模塊化的設(shè)計(jì)非常的好,因?yàn)樵试S高度代碼復(fù)用(前端和后端的共享優(yōu)化)。更多關(guān)于 LLVM 的資料請看這里。

適配不同的機(jī)器

在這一點(diǎn)上,你可能會想:

如果 LLVM 已經(jīng)夠模塊化,那么我們是否可以使用一個不同的后端,生成二進(jìn)制代碼,適配 OS X,iOS 或者是 Android?

假設(shè)是可以的,我們來看看如何實(shí)現(xiàn)。

手動構(gòu)建 Swift 代碼

如果使用 Xcode,系統(tǒng)會自動完成這些。我們現(xiàn)在需要手動編譯和連接一個簡單的 Swift "Hello world" :

// hello.swiftprint("Hello, world!");
構(gòu)建對象文件:
$ $SDK/usr/bin/swiftc -emit-object hello.swift
hello.o  里面到底有什么:
$ nm hello_swift.o
                 U __TFSSCfMSSFT21_builtinStringLiteralBp8byteSizeBw7isASCIIBi1__SS
                 U __TFSs27_allocateUninitializedArrayurFBwTGSaq__Bp_
                 U __TFSs5printFTGSaP__9separatorSS10terminatorSS_T_
                 U __TIFSs5printFTGSaP__9separatorSS10terminatorSS_T_A0_
                 U __TIFSs5printFTGSaP__9separatorSS10terminatorSS_T_A1_
0000000000000140 S __TMLP_
0000000000000100 S __TMaP_
                 U __TMdSS
                 U __TZvOSs7Process11_unsafeArgvGVSs20UnsafeMutablePointerGS0_VSs4Int8__
                 U __TZvOSs7Process5_argcVSs5Int32
                 U _globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_func6
                 U _globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_token6
0000000000000000 T _main
                 U _swift_getExistentialTypeMetadata
                 U _swift_once

看吧,這非常有趣。Swift mangles symbols 看起來明顯有點(diǎn)像 C++。事實(shí)上,print 函數(shù)并沒有成為_print symbol ,但是成為了更復(fù)雜的 symbol 的 __TFSs5printFTGSaP__9separatorSS10terminatorSS_T_ 列表。

同時也要求其他 symbols,主要是為了處理字符串轉(zhuǎn)換和內(nèi)存處理。

無論如何,所有這些 symbols 已經(jīng)在 libswiftCore.dylib 定義,也出現(xiàn)在 $SDK。我們現(xiàn)在要把這些信息給 linker:

$ ld -arch x86_64 -o hello hello.o
     -L$SDK/usr/lib/swift/macosx
     -lSystem -lswiftCore
 
$ DYLD_LIBRARY_PATH=$SDK/usr/lib/swift/macosx ./hello
Hello, world!

是的,這個方法是可行的。

適配 Android

現(xiàn)在最大的問題是 SwiftCore 庫缺失?,F(xiàn)在蘋果已經(jīng)為 iOS,OS X 和 Watch OS 都提供了一個。但是,很明顯,并沒有提供 Android 版本。

但是,不是所有 Swift 代碼都要求 SwiftCore 庫,跟不是所有 C++ 代碼都要求 STL 一樣。所以只要使用 Swift 的子集,不需要 SwiftCore 的那部分,這問題就算解決了。

為了演示,我們先來一個簡單的:

// add.swiftfunc addTwoNumbers(first: UInt8, second: UInt8) -> UInt8 {
  return first + second}

所以這過程基本分為 3 個步驟:

  1. 讓 Swift 編譯器生成一些 LLVM-IR

  2. 使用 LLVM 從中間表示的代碼生成 ARM ELF 

  3. 使用 Android NDK 來生成一個二進(jìn)制代碼,連接到已生成的對象文件

1. 讓 Swift 編譯器生成一些 LLVM-IR

在之前的步驟中,當(dāng)運(yùn)行 swiftc hello.swift,Swift 編譯器實(shí)際在干兩件事情: 

  1. 從 Swift 代碼中生成 LLVM 中間表示代碼

  2. 轉(zhuǎn)換 IR 為一些 x86_64 機(jī)器代碼,打包為一個 Mach-O 文件

這個實(shí)際上是非常常用的事例,所以編譯器可以一次性做完這些。但是我們想要生成一些 ARM ELF 文件 (在 Android 上使用的二進(jìn)制格式文件)。

$SDK/usr/bin/swiftc
  -parse-as-library # We don't need a "main" function
  -target armv7-apple-ios9.0
  -emit-ir
  add.swift
  | grep -v "^!" # Filter-out iOS metadata
  > add.ll

注意:我們需要添加 "grep" 過濾器來移除一些  iOS 特定的元數(shù)據(jù)(Swift 編譯器加進(jìn)去的) 。

2. 從 LLVM-IR 中生成一個對象文件

在這點(diǎn)上,我們需要 Android NDK。非常幸運(yùn)的是已經(jīng)包括了一個 LLVM 工具鏈,我們可以利用  llc(LLVM static compiler) :

$NDK/toolchains/llvm-3.5/prebuilt/darwin-x86_64/bin/llc
  -mtriple=armv7-none-linux-androideabi
  -filetype=obj
  add.ll

非常棒,所以我們已經(jīng)構(gòu)建了一個 ARM ELF 對象文件!

3. 打包一個 Android 應(yīng)用的對象文件

我們需要從 Java 中調(diào)用它,所以需要一個 JNI bridge。這使用 C 來編寫非常簡單:

// jni-bridge.c// Let's work around Swift symbol mangling#define SWIFT_ADD _TF3add13addTwoNumbersFTVSs5UInt86secondS0__S0_uint8_t SWIFT_ADD(uint8_t, uint8_t);jstring jni_bridge(JNIEnv * env, jobject thiz ) {
  uint8_t a = 123;
  uint8_t b = 45;
  uint8_t c = SWIFT_ADD(a,b);
 
  char result[255];
  sprintf(result, "The result is %d", c);
 
  return (*env)->NewStringUTF(env, result);}
最后,我們需要打包所有,變成一個共享庫:
$NDK_GCC/bin/arm-linux-androideabi-ld
  add.o
  jni_bridge.o
  -shared # Build a shared library
  -lc # We'll need the libc
  -L$NDK/platforms/android-13/arch-arm/usr/lib
就是這樣!我們需要打包,在一個 Android 應(yīng)用中分享對象文件,然后運(yùn)行:

通過 LLVM 在 Android 上運(yùn)行 Swift 代碼

總結(jié)

這非常有趣,但是并沒有什么用:

  • 一般來講,NDK 只是對一小部分的應(yīng)用有意義,所以情況的 Google 反對使用 NDK 編寫整個 Android 應(yīng)用。

  • 而且,因?yàn)槲覀內(nèi)笔?SwiftCore 庫,所以有了一定的限制,只適用于一小部分的 Swift 子集。

最后,很重要的一點(diǎn),這個示例已經(jīng)放到了 GitHub。

via romain.goyet.com


您還未登錄,請先登錄

熱門帖子

最新帖子

?