前言 本章对应官方教程第8章 。本章介绍如何将语言编译为目标文件。
教程如下:
教你使用swift写编译器玩具(0)
教你使用swift写编译器玩具(1)
教你使用swift写编译器玩具(2)
教你使用swift写编译器玩具(3)
教你使用swift写编译器玩具(4)
教你使用swift写编译器玩具(5)
教你使用swift写编译器玩具(6)
教你使用swift写编译器玩具(7)
教你使用swift写编译器玩具(8)
仓库在这
开始 因为我们之前用了JIT
,但是我们现在也要生成目标文件,两者只能选其一,所以我们现在把封装一下JIT
以及main文件。
首先是封装JIT
,我这里封装为类CodeRunner
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class CodeRunner { private let jit: JIT private typealias fnPr = @convention(c) () -> Double init (machine: TargetMachine ) { jit = JIT (machine: machine) } public func run (module: Module) { do { let handle = try jit.addEagerlyCompiledIR(module, { (_ ) -> JIT .TargetAddress in return JIT .TargetAddress () }) let addr = try jit.address(of: "__anon_expr" ) let fn = unsafeBitCast (addr, to: fnPr.self ) print ("\(fn()) " ) try jit.removeModule(handle) } catch { fatalError ("Adds the IR from a given module failure." ) } } }
接着是main文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 let isUseJIT = false func readFile (_ path: String) -> String ? { var path = path if path.hasSuffix("\n " ) { path.removeLast() } guard path.split(separator: "." ).last! == "k" else { print ("Expected file is *.k." ) return nil } do { return try String (contentsOfFile: path, encoding: .utf8) } catch { print ("Read file \(path) failure." ) return nil } }func main () { initModule() let parser = Parser () if let path = String (data: FileHandle .standardInput.availableData, encoding: .utf8) { if let str = readFile(path) { parser.parse(str) theModule.targetTriple = .default do { let targetMachine = try TargetMachine (triple: .default, cpu: "x86-64" , features: "" , optLevel: .default, relocations: .default, codeModel: .default) theModule.dataLayout = targetMachine.dataLayout let pass = PassPipeliner (module: theModule) pass.execute() if isUseJIT { let runner = CodeRunner (machine: targetMachine) runner.run(module: theModule) } else { let path = "填你自己的路径" try targetMachine.emitToFile(module: theModule, type: .object, path: path) print ("Wrote \(path) " ) } } catch { print ("\(error) " ) } } } } main()
测试 我们新建.k文件average.k
。
1 def average(x y ) (x + y ) * 0.5 ;
我们运行代码生成目标文件output.o
,我们可以用下面命令看一下目标文件是否生成了符号表。
会输出类似的以下信息。
1 2 3 4 output .o: file format Mach-O 64-bit x86-64 SYMBOL TABLE : 0000000000000000 g F __TEXT,__text _average
生成完目标文件我们需要写一段C++代码进行调用。
1 2 3 4 5 6 7 8 9 10 #include <iostream> extern "C" { double average (double , double ) ; }int main () { std ::cout << "average of 3.0 and 4.0: " << average(3.0 , 4.0 ) << std ::endl ; }
将程序链接到output.o并查看结果
1 2 3 4 5 clang++ main.cpp output.o -o main ./main //输出 average of 3.0 and 4.0: 3.5
完整代码请参考仓库 。
可能遇到的问题 macOS下’wchar.h’ File Not Found
参考回答macOS ‘wchar.h’ File Not Found
1 open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg
Mojava下安装头文件包即可。