-
-
[原创]LLVMlite学习笔记之1:建立函数和调用函数
-
发表于: 2022-7-22 05:10 5591
-
llvmlite是llvm的一个简化版本,他使用python来生成LLVM IR,首先我们熟悉一下整个编译过程。
1,拿到llvmir:
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 | import llvmlite.ir as ir# 创建一个int64类型的变量int64 = ir.IntType(64)# 你也可以使用这个来创建一个浮点类型的变量,不过浮点类型的变量就不可以传参了double = ir.DoubleType()# 创建一个module,每一个module就相当于一个。c文件一样,# 其实不一样,我这么说是为了方便理解# 到时候你就拿这个module去编译的module = ir.Module(name=__file__)# 有了模块我们需要创建一个函数,func就是我们创建出来的函数fnty = ir.FunctionType(int64, (int64, int64))func = ir.Function(module, fnty, name="fpadd")# 这个func是一个对象,现在我们给他添加一个basic block# 我们得到了builder,下面我就是拿builder做一些操作了block = func.append_basic_block(name="entry")builder = ir.IRBuilder(block)# 给这个block添加一个add指令a, b = func.argsresult = builder.add(a, b, name="res")# 将这个指令returnbuilder.ret(result)# 现在我们打印出来看看这个print(module) |
打印出来的结果:
1 2 3 4 5 6 7 8 9 10 11 | root@localhost:~/vex2tfa/tests# python3 getIR.py; ModuleID = "getIR.py"target triple = "unknown-unknown-unknown"target datalayout = ""define i64 @"fpadd"(i64 %".1", i64 %".2"){entry: %"res" = add i64 %".1", %".2" ret i64 %"res"} |
至此我们创建了一个带加法指令的函数在llvm ir中
2,编译module
ir已经被创建好了,我们需要去编译他,废话不多说直接上代码。
首先,我们补上一些import,(注意 from这种必须第一行)
1 2 3 | from __future__ import print_functionfrom ctypes import CFUNCTYPE, c_int64import llvmlite.binding as llvm |
然后再将下面的代码补到print(module)之后
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 | llvm_ir = str(module)# 从这里开始到一直到# func_ptr = engine.get_function_address("fpadd")# 你可以看作是一个整体,把编译好的module变成字符串,然后再送入引擎编译# 下边的init代码你最好直接复制好了,一个非常标准的模版# 实际上就是一些类传来传去,我们只需要关注上半部分就行了# All these initializations are required for code generation!llvm.initialize()llvm.initialize_native_target()llvm.initialize_native_asmprinter() # yes, even this onedef create_execution_engine(): """ Create an ExecutionEngine suitable for JIT code generation on the host CPU. The engine is reusable for an arbitrary number of modules. """ # Create a target machine representing the host target = llvm.Target.from_default_triple() target_machine = target.create_target_machine() # And an execution engine with an empty backing module backing_mod = llvm.parse_assembly("") engine = llvm.create_mcjit_compiler(backing_mod, target_machine) return enginedef compile_ir(engine, llvm_ir): """ Compile the LLVM IR string with the given engine. The compiled module object is returned. """ # Create a LLVM module object from the IR mod = llvm.parse_assembly(llvm_ir) mod.verify() # Now add the module and make sure it is ready for execution engine.add_module(mod) engine.finalize_object() engine.run_static_constructors() return mod# 前面的准备工作做好了,这里就是开始编译# 编译好了以后就是拿engine做操作,和mod无关了engine = create_execution_engine()mod = compile_ir(engine, llvm_ir)# Look up the function pointer (a Python int)# 拿到fpadd的函数地址func_ptr = engine.get_function_address("fpadd")# Run the function via ctypes# 这个地方未来可能需要管一下,中间的两个函数其实是不需要管的,# 不管你的module长的啥样子,中间的两个函数create_execution_engine# 和compile_ir都不会有任何变化,# 所以俺推荐你直接复制粘贴中间的这一堆东西行了cfunc = CFUNCTYPE(c_int64, c_int64, c_int64)(func_ptr)res = cfunc(1, 3)print("fpadd(...) =", res) |
然后俺编译一下得到了如下输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 | root@localhost:~/vex2tfa/tests# python3 getir.py; ModuleID = "getir.py"target triple = "unknown-unknown-unknown"target datalayout = ""define i64 @"fpadd"(i64 %".1", i64 %".2"){entry: %"res" = add i64 %".1", %".2" ret i64 %"res"}fpadd(...) = 4 |
3, 添加其他函数
我们已经看到了这个基本的流程,从开始创建module,再到创建函数,在到创建basic block,最后创建instruction。这个都是基于官方文档复习,俺在这里没有做太多的更改,仅仅熟悉了一个流程,仅仅知道这个流程是不够的,不然怎么能够成长为高级程序员呢,下面我们需要做一些其他的操作,添加一个新的函数,然后在这个旧的函数中去call这个新的函数。
我们在module = ir.Module(name=file)这一行下边添加以下内容。
1 2 3 4 5 6 7 8 9 10 11 12 | # 创建一个名字为add2的函数,(int64, int64)表示这个函数接收两个类型为int64的参数func2 = ir.Function(module, ir.FunctionType(int64, (int64, int64)), "add2{}".format(64))#创建一个名字为entry2的basic blockblock2 = func2.append_basic_block(name="entry2")# 将这个创建好的basic block送入builderbuilder2 = ir.IRBuilder(block2)# 拿到这个函数的两个参数c, d = func2.args# 添加一个减法指令result2 = builder2.sub(d, c, name="res2")# 将这个减出来的数字返回builder2.ret(result2) |
这个时候我们就创建了一个名为add2的带一个减法指令的函数
我们怎么去call这个函数呢?
我们需要在result = builder.add(a, b, name="res")这一行下添加一个一个call指令
1 | result_of_call = builder.call(func2, [result, b]) |
然后修改返回值
1 | builder.ret(result) 改为 builder.ret(result_of_call) |
改好了以后我们打印一下输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | root@localhost:~/vex2tfa/tests# python3 getir.py; ModuleID = "getir.py"target triple = "unknown-unknown-unknown"target datalayout = ""define i64 @"add264"(i64 %".1", i64 %".2"){entry2: %"res2" = sub i64 %".2", %".1" ret i64 %"res2"}define i64 @"fpadd"(i64 %".1", i64 %".2"){entry: %"res" = add i64 %".1", %".2" %".4" = call i64 @"add264"(i64 %"res", i64 %".2") ret i64 %".4"}fpadd(...) = -1 |
我们发现结果变成了-1, 那是因为
1 + 3 = 4
3 - 4 = -1
好啦,如果创建函数和调用已经创建的函数我们基本上已经会了,下一期俺将介绍如何在IR中创建if_else语句
喜欢交流的人可以加秋秋裙:375678777
赞赏
他的文章
赞赏
雪币:
留言: