alloca指令
alloca
指令用来申请内存。会在你当前执行函数所在的栈中砍一块内存,内存大小为sizeof(<type>)*NumElements
,等函数执行结束,申请的这块内存就自动释放了。也就是说alloca
指令申请的内存,函数不退栈,内存不释放。
为什么会引发内存泄漏
我们在做编译(codeGen)的时候,表达式不得不产生一些中间变量,如果表达式位于循环体中,循环次数又较多,很容易造成内存泄漏。
while(i <= n){
sum = sum + i;
i = i + 1;
}
上面循环语句的循环体中有两个赋值语句,这两个赋值语句在codeGen的时候会分别产生sum + i
,1
,i + 1
,三个值的中间变量对应的alloca
指令,如果我们将alloca
写在循环体中,在程序运行时每循环一次,都会执行三个中间变量对应的alloca
指令,而每次alloca
完内存都不会释放,如此堆积,就会造成程序内存泄漏。下面是对应的IR伪代码:
;sum + i, 1, i + 1 三个值对应的中间变量分别是%sum_i, %1_, %i_1
entry:
%i = alloca i32
%sum = alloca i32
%n = alloca i32
...
br label %while_count
while_count:
...
br ..., label %while_body, label %while_end
while_body:
; 一系列操作
%sum_i = alloca i32
%1_ = alloca i32
%i_1 = alloca i32
store sum_i中存放的值, %sum
%value = add %i中存放的值, %1_中存放的值
store %value, %i_1
store %i_1中存放的值, %i
...
br label %while_count
while_end:
...
如何避免内存泄漏
llvm函数中的第一个基本块有两个特殊点:只要进入该函数就立刻执行第一个基本块,并且不允许有祖先基本块(即不能有任何分支到函数的入口块)。 所以只要将llvm函数中所有的alloca
指令放到函数的第一个基本块中,就可以有效避免运行时因为循环执行alloca
指令导致内存泄漏的问题。
将上面的IR伪代码修改成如下,alloca
指令都写在entry
基本块中:
;sum + i, 1, i + 1 三个值对应的中间变量分别是%sum_i, %1_, %i_1
entry:
%i = alloca i32
%sum = alloca i32
%n = alloca i32
...
%sum_i = alloca i32
%1_ = alloca i32
%i_1 = alloca i32
br label %while_count
while_count:
...
br ..., label %while_body, label %while_end
while_body:
; 一系列操作
store sum_i中存放的值, %sum
%value = add %i中存放的值, %1_中存放的值
store %value, %i_1
store %i_1中存放的值, %i
...
br label %while_count
while_end:
...