使用llvm的alloca指令创建变量时如何巧妙避免内存泄漏

alloca指令

alloca指令用来申请内存。会在你当前执行函数所在的栈中砍一块内存,内存大小为sizeof(<type>)*NumElements,等函数执行结束,申请的这块内存就自动释放了。也就是说alloca指令申请的内存,函数不退栈,内存不释放。

为什么会引发内存泄漏

我们在做编译(codeGen)的时候,表达式不得不产生一些中间变量,如果表达式位于循环体中,循环次数又较多,很容易造成内存泄漏。

while(i <= n){
    
    
		sum = sum + i;
		i = i + 1;
	}

上面循环语句的循环体中有两个赋值语句,这两个赋值语句在codeGen的时候会分别产生sum + i1i + 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: 
  ...

猜你喜欢

转载自blog.csdn.net/qq_42570601/article/details/114080858
今日推荐