我已经开始玩 LLVM,制作一种宠物语言。我正在使用 C-API。我有一个解析器和基本的 AST,但我在 LLVM 方面遇到了一些障碍。
以下是我的代码的缩小版本,用于说明我当前的问题:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "llvm-c/Core.h"
#include "llvm-c/ExecutionEngine.h"
#include "llvm-c/Target.h"
#include "llvm-c/Analysis.h"
#include "llvm-c/BitWriter.h"
static LLVMModuleRef mod;
static LLVMBuilderRef builder;
static LLVMExecutionEngineRef engine;
typedef struct oper_t {
const char * name;
LLVMTypeRef args[2];
LLVMTypeRef ret;
LLVMValueRef val;
} oper_t;
#define NUM_OPER 2
static oper_t oper[NUM_OPER] = {
{ .name = "function1" },
{ .name = "function2" },
};
void codegen_init(const char * filename)
{
char *error;
mod = LLVMModuleCreateWithName(filename);
builder = LLVMCreateBuilder();
error = NULL;
LLVMVerifyModule(mod, LLVMAbortProcessAction, &error);
if(error) printf("LLVM init Verify message \"%s\"\n", error);
LLVMDisposeMessage(error);
error = NULL;
LLVMLinkInMCJIT();
LLVMInitializeNativeTarget();
LLVMInitializeNativeAsmPrinter();
if (LLVMCreateExecutionEngineForModule(&engine, mod, &error) != 0)
{
fprintf(stderr, "LLVM failed to create execution engine\n");
abort();
}
if(error)
{
printf("LLVM Execution Engine message %s\n", error);
LLVMDisposeMessage(error);
exit(EXIT_FAILURE);
}
}
int runOper(oper_t * o, long a, long b)
{
LLVMValueRef v, l, r;
o->args[0] = LLVMInt32Type();
o->args[1] = LLVMInt32Type();
o->ret = LLVMFunctionType(LLVMInt32Type(), o->args, 2, 0);
o->val = LLVMAddFunction(mod, o->name, o->ret);
LLVMBasicBlockRef entry = LLVMAppendBasicBlock(o->val, "entry");
LLVMPositionBuilderAtEnd(builder, entry);
l = LLVMConstInt(LLVMInt32Type(), a, 0);
r = LLVMConstInt(LLVMInt32Type(), b, 0);
v = LLVMBuildAdd(builder, l, r, "add");
LLVMBuildRet(builder, v);
char *error = NULL;
LLVMVerifyModule(mod, LLVMAbortProcessAction, &error);
if(error) printf("LLVM func Verify message \"%s\"\n", error);
LLVMDisposeMessage(error);
LLVMGenericValueRef g = LLVMRunFunction(engine, o->val, 0, NULL);
printf("LLVM func executed without crash\n");
LLVMDeleteFunction(o->val);
return (long)LLVMGenericValueToInt(g, 1);
}
int main(int argc, char const *argv[])
{
long val;
codegen_init("test");
val = runOper(&oper[0], 3, 4);
printf("3 + 4 is %ld\n", val);
val = runOper(&oper[1], 6, 7);
printf("6 + 7 is %ld\n", val);
}
我可以使用以下命令编译它:
gcc test.c `llvm-config --cflags --cppflags --ldflags --libs core executionengine mcjit interpreter analysis native bitwriter --system-libs` -o test.exe
或者,我也尝试过:
gcc `llvm-config --cflags --cppflags` -c test.c
g++ test.o `llvm-config --cxxflags --ldflags --libs core executionengine mcjit interpreter analysis native bitwriter --system-libs` -o test.exe
无论哪种方式,我都会得到这个结果:
$ ./test.exe
LLVM init Verify message ""
LLVM func Verify message ""
LLVM func executed without crash
3 + 4 is 7
LLVM func Verify message ""
Segmentation fault
我也尝试过使用 clang 来衡量。
显然我在滥用 LLVM C-API。我主要是在努力了解何时可以安全调用 API 函数,以及何时可以安全地释放/删除 LLVM 引用的内存。例如LLVMTypeRef args[2]
参数,我在 LLVM C-API 源代码中LLVMFunctionType
看到它正在为 args 参数创建一个 ArrayRef。这意味着我必须坚持使用 args 参数,直到 LLVM 完成它。我真的不知道那是什么时候。(我打算在堆上分配这块内存)
简单地说, 如果有人不能在这个例子中解释我做错了什么,而是更根本地解释我应该如何找出我做错了什么,我会喜欢它。
LLVM C-API 文档对API 中可用的函数进行了很好的细分,但我没有发现它对应该如何调用 API 函数给出太多描述,即。什么顺序是安全的/预期的。
我还发现这个文档很有帮助,因为它可以很容易地搜索到单个函数原型。但同样它没有给出如何使用 C-API 的上下文或示例。
最后我不得不参考Paul Smith 的博客,它现在有点过时了,但绝对是我能走到这一步的原因。
PS我不希望一切都为我拼写出来,我只是想建议如何自学LLVM