基于LLVM-增加JIT支持

JIT其实就是Just-In-Time也就是即时编译,在程序运行的时候会将代码翻译成机器码并且去执行,与之相对的就是AOT(Ahead Of Time),它在程序运行之前就会将代码翻译成机器码,JIT结合了AOT和解释执行的优势,它能够产生高效的机器码,并且具备足够的灵活性

首先我们定义一个执行引擎作为全局静态变量

static ExecutionEngine *TheExecutionEngine;

然后就是在main函数当中增加如下代码

  //InitializeNativeTarget——主程序应该调用此函数来初始化与主机对应的本机目标。这对于JIT应用程序非常有用,以确保目标被正确地链接。客户端对这个函数进行多次调用是合法的。
    InitializeNativeTarget();
    //主程序应该调用此函数来初始化本机目标asm打印机
    InitializeNativeTargetAsmPrinter();
    //InitializeNativeTargetAsmParser——主程序应该调用这个函数来初始化本机目标asm解析器。
    InitializeNativeTargetAsmParser();

还有下面的代码

 std::unique_ptr<Module> Owner = make_unique<Module>("my compiler", *llvmcx);
    Module_Ob = Owner.get();

    std::string ErrStr;
    TheExecutionEngine =
    EngineBuilder(std::move(Owner))
    .setErrorStr(&ErrStr)
    .setMCJITMemoryManager(llvm::make_unique<SectionMemoryManager>())
    .create();

最后修改顶级表达式的解析器,修改为如下所示

//封装的函数
static void HandleTopExpression() {
    if(FunctionDefnAST *F = top_level_parser()) {
        if(Function *LF = F->Codegen()) {
            TheExecutionEngine->finalizeObject();
            void *FPtr = TheExecutionEngine->getPointerToFunction(LF);

            double (*FP)() = (double (*)())(intptr_t)FPtr;
            fprintf(stderr, "Evaluated to %f\n", FP());
        }
    }
    else {
        next_token();
    }
}

去分析4+5;的表达式,得到的结果如下所示

Evaluated to 0.000000
; ModuleID = 'my compiler'
source_filename = "my compiler"
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

define i32 @0() {
entry:
  ret i32 9
}

LLVM当中的JIT编译器会匹配本机的ABI平台,它会把得到的指针转成相应类型的函数指针,然后直接去调用,这样JIT编译得到的代码与静态编译链接的本地机器码没有区别

关于ABI平台其实就是应用程序二进制接口(ABI-Application Binary Interface)定义了一组在PowerPC系统软件上编译应用程序所需要遵循的一套规则。主要包括基本数据类型,通用寄存器的使用,参数的传递规则,以及堆栈的使用等等

ABI涵盖了各种细节:如数据类型、大小和对齐;调用约定(控制着函数的参数如何传送以及如何接受返回值);系统调用的编码和一个应用如何向操作系统进行系统调用;以及在一个完整的操作系统ABI中,目标文件的二进制格式、程序库等等。一个完整的ABI,像Intel二进制兼容标准 (iBCS)[1] ,允许支持它的操作系统上的程序不经修改在其他支持此ABI的操作体统上运行

猜你喜欢

转载自blog.csdn.net/zcmuczx/article/details/80839349
JIT