我正在学习llvm并希望对我的想法进行概念验证。从ExecutionEngine调用C/C++函数
基本上,我想拆分我的编译器和我的运行时。编译器会给出一个.bc,运行时会通过ParseBitcodeFile加载它,并使用ExecutionEngine来运行它。这部分工作。我希望能够在我的运行时实现所有系统调用(文件io,标准输出打印等)的C/C++函数。我的问题是,我怎么能从我的玩具编译器的代码中调用这些函数,这个编译器在llvm的另一个步骤中编译,并允许它在执行时使用。
我正在学习llvm并希望对我的想法进行概念验证。从ExecutionEngine调用C/C++函数
基本上,我想拆分我的编译器和我的运行时。编译器会给出一个.bc,运行时会通过ParseBitcodeFile加载它,并使用ExecutionEngine来运行它。这部分工作。我希望能够在我的运行时实现所有系统调用(文件io,标准输出打印等)的C/C++函数。我的问题是,我怎么能从我的玩具编译器的代码中调用这些函数,这个编译器在llvm的另一个步骤中编译,并允许它在执行时使用。
好消息:使用JIT ExecutionEngine
时,这将只是工作。当JIT-er发现IR使用的外部符号在IR本身中找不到时,它会在JIT进程本身中查找,因此可以调用从主机程序中可见的任何符号。
这在part 4 of the LLVM tutorial直接解释:
哇,怎么了JIT了解正弦和余弦?答案是 令人惊讶的简单:在这个例子中,JIT开始执行一个 函数并进入一个函数调用。它意识到功能是 还没有JIT编译和调用标准的例程集到 来解析函数。在这种情况下,没有为 函数定义主体,因此JIT最终在“万花筒”过程本身上调用了“dlsym(”sin“)”。由于“罪恶”是在JIT的地址空间中定义的,因此它只是将模块中的调用修正为直接调用 libm版本。
对于血淋淋的细节看lib/ExecutionEngine/JIT/JIT.cpp
- 特别是DynamicLibrary
它的用法。
Eli的回答非常好,你应该接受它。还有另一种方法,即将运行时的源文件分别编译为LLVM模块(例如使用Clang)并使用ExecutionEngine::addModule()
来添加它们。
它不太方便,它意味着编译两次相同的文件(一次用于您的主机程序,另一次从中获取Module
),但其优点是它可以从JITted代码启用内联和其他跨功能优化。