美文网首页使用swift写编译器玩具
教你使用swift写编译器玩具(8)

教你使用swift写编译器玩具(8)

作者: QYiZHong | 来源:发表于2019-08-10 13:17 被阅读0次

    前言

    本章对应官方教程第8章。本章介绍如何将语言编译为目标文件。

    教程如下:

    教你使用swift写编译器玩具(0)

    教你使用swift写编译器玩具(1)

    教你使用swift写编译器玩具(2)

    教你使用swift写编译器玩具(3)

    教你使用swift写编译器玩具(4)

    教你使用swift写编译器玩具(5)

    教你使用swift写编译器玩具(6)

    教你使用swift写编译器玩具(7)

    教你使用swift写编译器玩具(8)

    仓库在这

    开始

    因为我们之前用了JIT,但是我们现在也要生成目标文件,两者只能选其一,所以我们现在把封装一下JIT以及main文件。

    首先是封装JIT,我这里封装为类CodeRunner

    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文件。

    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() {
        //初始化Module和中间代码优化器
        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 {
                    //这个初始化方法里已经调用了initializeLLVM()
                    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

    def average(x y) (x + y) * 0.5;
    

    我们运行代码生成目标文件output.o,我们可以用下面命令看一下目标文件是否生成了符号表。

    objdump -t output.o
    

    会输出类似的以下信息。

    output.o:   file format Mach-O 64-bit x86-64
    
    SYMBOL TABLE:
    0000000000000000 g     F __TEXT,__text  _average
    

    生成完目标文件我们需要写一段C++代码进行调用。

    #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并查看结果

    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

    open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg
    

    Mojava下安装头文件包即可。

    相关文章

      网友评论

        本文标题:教你使用swift写编译器玩具(8)

        本文链接:https://www.haomeiwen.com/subject/tycrjctx.html