Llvm 生成 IR 分段错误(核心已转储)
Llvm generation IR segmentation fault (core dumped)
我正在尝试将一个数组作为参数传递,并在另一个函数中使用这个数组,并使用像 C 这样的 Toy lang。
我的代码运行并且在我编译下面的代码时编译良好
int at_index(int a[], int index) {
return 0;
}
int main() {
int a[10];
int tmp;
tmp = at_index(a, 0);
print(tmp);
return 0;
}
我的编译器生成以下 IR
; ModuleID = 'MicrocC-module'
source_filename = "MicrocC-module"
declare void @print(i32)
declare i32 @getint()
define i32 @at_index(i32* %0, i32 %1) {
entry:
%a = alloca i32*
store i32* %0, i32** %a
%index = alloca i32
store i32 %1, i32* %index
ret i32 0
}
define i32 @main() {
entry:
%a = alloca [10 x i32]
%tmp = alloca i32
%a1 = getelementptr inbounds [10 x i32], [10 x i32]* %a, i32 0, i32 0
%0 = call i32 @at_index(i32* %a1, i32 0)
store i32 %0, i32* %tmp
%tmp2 = load i32, i32* %tmp
call void @print(i32 %tmp2)
ret i32 0
}
但是如果我尝试在函数中使用数组 an 我从编译器收到核心转储,我的演示有核心转储是
int at_index(int a[], int index) {
a[0] = 0;
return 0;
}
int main() {
int a[10];
int tmp;
tmp = at_index(a, 0);
print(tmp);
return 0;
}
我开始调试代码,但我找不到错误,也许我从代码中访问数组是错误的。
我访问数组位置的一般代码是使用 Ocaml
调用以下 LLVM API
我从 AST 接收到一个以 access operation
作为变量和 index
的节点,我调用以下 API.
Llvm.build_in_bounds_gep variable [0, index] "access" llvm_builder
根据这个调用的结果,我对变量进行了所有操作,但我认为当数组作为参数时函数体的情况,我的变量是一个指针,因此,我收到错误,但这是我的想法,这是我卡住的地方,知道吗?要访问作为参数的数组,还需要执行一些额外的操作吗?
更新
我生成函数调用的代码部分是。
在函数声明中生成分配和存储的规则
let rec translate_stm_to_llvm_ir llvm_builder stm_def =
match stm_def.node with
| Ast.Dec(tipe, id) ->
begin
logger#debug "Declaration stm processing for type %s" (Ast.show_typ tipe);
match tipe with
| Ast.TypArray(arr_tipe, size) ->
begin
let llvm_arr = gen_array_type (to_llvm_type arr_tipe) size in
let llvm_val = Llvm.build_alloca llvm_arr id llvm_builder in
(>->) id llvm_val false
end
| _ ->
begin
logger#trace "Literal variable build with LLVM";
let all_llvm = Llvm.build_alloca (to_llvm_type tipe) id llvm_builder in
(>->) id all_llvm false
end
end
我如何在函数调用期间转换参数,例如:为 int array[]
创建一个指针并为 a[i]
创建一个值
and translate_fun_exp_to_llvm exp llvm_builder =
match exp.node with
| Assign(variable, exp) ->
begin
logger#trace "*Assign stm* translating ...";
let llvm_var = translate_acc_var variable llvm_builder in
let exp_to_assign = translate_exp_to_llvm exp llvm_builder in
let _ = Llvm.build_store exp_to_assign llvm_var llvm_builder in
Llvm.build_load llvm_var "" llvm_builder
end
| Access(access) ->
begin
match access.node with
| Ast.AccVar(id) ->
begin
logger#error "Access on var inside a function call";
try
let acc_var = (<-<) id in
let type_var = Llvm.type_of acc_var in
let name_var = Llvm.value_name acc_var in
match (Llvm.string_of_lltype type_var) with
| "i32*" | "i8*" | "i1*" ->
logger#error "lvalue in function call";
Llvm.build_load acc_var name_var llvm_builder
| _ ->
begin
logger#trace "Access to first element of the array";
let first_pos = Llvm.const_int gen_int_type 0 in
Llvm.build_in_bounds_gep acc_var (Array.of_list [first_pos; first_pos]) name_var llvm_builder
end
with Not_found -> failwith "Variable not found"
end
| _ ->
begin
let acc_var = translate_acc_var access llvm_builder in
Llvm.build_load acc_var "" llvm_builder
我如何翻译函数中的访问变量
and translate_acc_var acc_def llvm_builder =
match acc_def.node with
| Ast.AccVar(id) ->
begin
logger#trace "Access variable with id %s ...." id;
try (<-<) id
with Not_found -> failwith "Variable not found"
end
| AccIndex(acc, index) ->
begin
let variable = translate_acc_var acc llvm_builder in
let index_exp = translate_exp_to_llvm index llvm_builder in
let zeros_pos = Llvm.const_int gen_int_type 0 in
Llvm.build_in_bounds_gep variable (Array.of_list([zeros_pos; index_exp])) "" llvm_builder
end
| AccDeref (expr) as node->
begin
logger#debug "* *%s * Translating ..." (show_access_node node);
let llval = translate_exp_to_llvm expr llvm_builder in
let val_name = Llvm.value_name llval in
let type_val = Llvm.type_of llval in
Llvm.build_ptrtoint llval type_val val_name llvm_builder
end
| _ -> failwith "Access var not implemented"
其中 AST 由以下规则组成
type typ =
| TypInt (* Type int *)
| TypBool (* Type bool *)
| TypChar (* Type char *)
| TypArray of typ * int option (* Array type *)
| TypPoint of typ (* Pointer type *)
| TypVoid (* Type void *)
[@@deriving show]
and expr = expr_node annotated_node
and expr_node =
| Access of access (* x or *p or a[e] *)
| Assign of access * expr (* x=e or *p=e or a[e]=e *)
| Addr of access (* &x or &*p or &a[e] *)
| ILiteral of int (* Integer literal *)
| CLiteral of char (* Char literal *)
| BLiteral of bool (* Bool literal *)
| UnaryOp of uop * expr (* Unary primitive operator *)
| BinaryOp of binop * expr * expr (* Binary primitive operator *)
| Call of identifier * expr list (* Function call f(...) *)
[@@deriving show]
and access = access_node annotated_node
and access_node =
| AccVar of identifier (* Variable access x *)
| AccDeref of expr (* Pointer dereferencing *p *)
| AccIndex of access * expr (* Array indexing a[e] *)
[@@deriving show]
通过引入记录器,我在调用 getelementptr inbounds
之前获取最后一个值
我现在的价值是
[0.042 Trace CodeGen ] Variable -> %2 = load i32*, i32** %a
[0.042 Trace CodeGen ] index -> %3 = load i32, i32* %index
查看您的代码,看起来 variable
指的是 %a
,类型 i32**
的 alloca
,index
指的是 %index
,类型为 %i32
的 alloca
。这引入了两个问题:
- GEP 只是一个地址计算 - 它不会取消引用任何东西。因此你不能用它来通过这样的双指针。您需要首先使用
load
取消引用 alloca
,然后在 GEP 中使用加载的结果。然后您还应该删除 0
,因为在 load
之后只有一个指针,只需要一个索引。
- GEP 中的索引必须是整数,而不是指向整数的指针。同样,您需要
load
索引并使用 GEP 中的加载结果。
我正在尝试将一个数组作为参数传递,并在另一个函数中使用这个数组,并使用像 C 这样的 Toy lang。
我的代码运行并且在我编译下面的代码时编译良好
int at_index(int a[], int index) {
return 0;
}
int main() {
int a[10];
int tmp;
tmp = at_index(a, 0);
print(tmp);
return 0;
}
我的编译器生成以下 IR
; ModuleID = 'MicrocC-module'
source_filename = "MicrocC-module"
declare void @print(i32)
declare i32 @getint()
define i32 @at_index(i32* %0, i32 %1) {
entry:
%a = alloca i32*
store i32* %0, i32** %a
%index = alloca i32
store i32 %1, i32* %index
ret i32 0
}
define i32 @main() {
entry:
%a = alloca [10 x i32]
%tmp = alloca i32
%a1 = getelementptr inbounds [10 x i32], [10 x i32]* %a, i32 0, i32 0
%0 = call i32 @at_index(i32* %a1, i32 0)
store i32 %0, i32* %tmp
%tmp2 = load i32, i32* %tmp
call void @print(i32 %tmp2)
ret i32 0
}
但是如果我尝试在函数中使用数组 an 我从编译器收到核心转储,我的演示有核心转储是
int at_index(int a[], int index) {
a[0] = 0;
return 0;
}
int main() {
int a[10];
int tmp;
tmp = at_index(a, 0);
print(tmp);
return 0;
}
我开始调试代码,但我找不到错误,也许我从代码中访问数组是错误的。
我访问数组位置的一般代码是使用 Ocaml
调用以下 LLVM API我从 AST 接收到一个以 access operation
作为变量和 index
的节点,我调用以下 API.
Llvm.build_in_bounds_gep variable [0, index] "access" llvm_builder
根据这个调用的结果,我对变量进行了所有操作,但我认为当数组作为参数时函数体的情况,我的变量是一个指针,因此,我收到错误,但这是我的想法,这是我卡住的地方,知道吗?要访问作为参数的数组,还需要执行一些额外的操作吗?
更新
我生成函数调用的代码部分是。
在函数声明中生成分配和存储的规则
let rec translate_stm_to_llvm_ir llvm_builder stm_def =
match stm_def.node with
| Ast.Dec(tipe, id) ->
begin
logger#debug "Declaration stm processing for type %s" (Ast.show_typ tipe);
match tipe with
| Ast.TypArray(arr_tipe, size) ->
begin
let llvm_arr = gen_array_type (to_llvm_type arr_tipe) size in
let llvm_val = Llvm.build_alloca llvm_arr id llvm_builder in
(>->) id llvm_val false
end
| _ ->
begin
logger#trace "Literal variable build with LLVM";
let all_llvm = Llvm.build_alloca (to_llvm_type tipe) id llvm_builder in
(>->) id all_llvm false
end
end
我如何在函数调用期间转换参数,例如:为 int array[]
创建一个指针并为 a[i]
and translate_fun_exp_to_llvm exp llvm_builder =
match exp.node with
| Assign(variable, exp) ->
begin
logger#trace "*Assign stm* translating ...";
let llvm_var = translate_acc_var variable llvm_builder in
let exp_to_assign = translate_exp_to_llvm exp llvm_builder in
let _ = Llvm.build_store exp_to_assign llvm_var llvm_builder in
Llvm.build_load llvm_var "" llvm_builder
end
| Access(access) ->
begin
match access.node with
| Ast.AccVar(id) ->
begin
logger#error "Access on var inside a function call";
try
let acc_var = (<-<) id in
let type_var = Llvm.type_of acc_var in
let name_var = Llvm.value_name acc_var in
match (Llvm.string_of_lltype type_var) with
| "i32*" | "i8*" | "i1*" ->
logger#error "lvalue in function call";
Llvm.build_load acc_var name_var llvm_builder
| _ ->
begin
logger#trace "Access to first element of the array";
let first_pos = Llvm.const_int gen_int_type 0 in
Llvm.build_in_bounds_gep acc_var (Array.of_list [first_pos; first_pos]) name_var llvm_builder
end
with Not_found -> failwith "Variable not found"
end
| _ ->
begin
let acc_var = translate_acc_var access llvm_builder in
Llvm.build_load acc_var "" llvm_builder
我如何翻译函数中的访问变量
and translate_acc_var acc_def llvm_builder =
match acc_def.node with
| Ast.AccVar(id) ->
begin
logger#trace "Access variable with id %s ...." id;
try (<-<) id
with Not_found -> failwith "Variable not found"
end
| AccIndex(acc, index) ->
begin
let variable = translate_acc_var acc llvm_builder in
let index_exp = translate_exp_to_llvm index llvm_builder in
let zeros_pos = Llvm.const_int gen_int_type 0 in
Llvm.build_in_bounds_gep variable (Array.of_list([zeros_pos; index_exp])) "" llvm_builder
end
| AccDeref (expr) as node->
begin
logger#debug "* *%s * Translating ..." (show_access_node node);
let llval = translate_exp_to_llvm expr llvm_builder in
let val_name = Llvm.value_name llval in
let type_val = Llvm.type_of llval in
Llvm.build_ptrtoint llval type_val val_name llvm_builder
end
| _ -> failwith "Access var not implemented"
其中 AST 由以下规则组成
type typ =
| TypInt (* Type int *)
| TypBool (* Type bool *)
| TypChar (* Type char *)
| TypArray of typ * int option (* Array type *)
| TypPoint of typ (* Pointer type *)
| TypVoid (* Type void *)
[@@deriving show]
and expr = expr_node annotated_node
and expr_node =
| Access of access (* x or *p or a[e] *)
| Assign of access * expr (* x=e or *p=e or a[e]=e *)
| Addr of access (* &x or &*p or &a[e] *)
| ILiteral of int (* Integer literal *)
| CLiteral of char (* Char literal *)
| BLiteral of bool (* Bool literal *)
| UnaryOp of uop * expr (* Unary primitive operator *)
| BinaryOp of binop * expr * expr (* Binary primitive operator *)
| Call of identifier * expr list (* Function call f(...) *)
[@@deriving show]
and access = access_node annotated_node
and access_node =
| AccVar of identifier (* Variable access x *)
| AccDeref of expr (* Pointer dereferencing *p *)
| AccIndex of access * expr (* Array indexing a[e] *)
[@@deriving show]
通过引入记录器,我在调用 getelementptr inbounds
我现在的价值是
[0.042 Trace CodeGen ] Variable -> %2 = load i32*, i32** %a
[0.042 Trace CodeGen ] index -> %3 = load i32, i32* %index
查看您的代码,看起来 variable
指的是 %a
,类型 i32**
的 alloca
,index
指的是 %index
,类型为 %i32
的 alloca
。这引入了两个问题:
- GEP 只是一个地址计算 - 它不会取消引用任何东西。因此你不能用它来通过这样的双指针。您需要首先使用
load
取消引用alloca
,然后在 GEP 中使用加载的结果。然后您还应该删除0
,因为在load
之后只有一个指针,只需要一个索引。 - GEP 中的索引必须是整数,而不是指向整数的指针。同样,您需要
load
索引并使用 GEP 中的加载结果。