如何在LLVM上创建Loop对象?

时间:2015-12-22 13:55:36

标签: compiler-construction llvm

我想知道LLVM如何创建Loop对象。

有许多与Loop相关的对象,如LoopInfo,LoopBase,Loop等。

但是我找不到LLVM源代码在创建这些对象的位置。

我想知道他们如何跟踪后沿,以及如何识别这是一个循环。

可以这么说,我想学习有关在LLVM上检测和分析循环信息的完整原则

2 个答案:

答案 0 :(得分:3)

有两种方法可以实现传统循环。一个不使用phi指令,但是,它们都使用br运算符。

看看以下代码:

#include <stdio.h>

int main () {

   for(int i = 0; i < 10; i++) {
      printf("Test.\n");
   }

   return 0;
}

在这里,我使用命令clang -S loop.c -emit-llvm生成了一个使用Clang的示例,使其实现了第一个选项:

@.str = private unnamed_addr constant [7 x i8] c"Test.\0A\00", align 1

; Function Attrs: nounwind
define i32 @main() #0 {
entry:
  %retval = alloca i32, align 4
  %i = alloca i32, align 4
  store i32 0, i32* %retval
  store i32 0, i32* %i, align 4
  br label %for.cond

for.cond:                                         ; preds = %for.inc, %entry
  %0 = load i32* %i, align 4
  %cmp = icmp slt i32 %0, 10
  br i1 %cmp, label %for.body, label %for.end

for.body:                                         ; preds = %for.cond
  %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([7 x i8]* @.str, i32 0, i32 0)) #1
  br label %for.inc

for.inc:                                          ; preds = %for.body
  %1 = load i32* %i, align 4
  %inc = add nsw i32 %1, 1
  store i32 %inc, i32* %i, align 4
  br label %for.cond

for.end:                                          ; preds = %for.cond
  ret i32 0
}

; Function Attrs: nounwind
declare i32 @printf(i8*, ...) #0

首先,我们创建标识符来表示我们的i和返回值变量,然后将它们各自设置为0.它转到第一个标签并开始循环,从计算由{{得到的布尔值开始1}}指令,其中icmp <cond> <ty> <op1>, <op2> =“签名小于”。如果slt小于10,则true将存储在%i中,否则将存储false。 后面的%cmp指令的重载语法br定义如果br i1 <cond>, label <iftrue>, label <iffalse>为真,程序将跳转到%cmp标签,在本例中为{{1}否则,它将跳转到iftrue标签,在本例中为%for.body。总结一下,如果iffalse仍然小于10,该指令将进入循环体,如果它达到10,它将退出循环。有了%for.end指令的最终知识,程序其余部分的行为应该是显而易见的。

现在,虽然第二种方法要短得多,但稍微复杂一点。我使用Clang使用命令%i生成此代码,即在1级优化标志上进行标记,使其实现第二个选项:

br

忽略增量技术clang -S loop.c -emit-llvm -O1的有趣语法,它只是整数数学溢出错误处理。我们专注于@.str = private unnamed_addr constant [7 x i8] c"Test.\0A\00", align 1 @str = private unnamed_addr constant [6 x i8] c"Test.\00" ; Function Attrs: nounwind define i32 @main() #0 { entry: br label %for.body for.body: ; preds = %for.body, %entry %i.02 = phi i32 [ 0, %entry ], [ %inc, %for.body ] %puts = tail call i32 @puts(i8* getelementptr inbounds ([6 x i8]* @str, i32 0, i32 0)) %inc = add nuw nsw i32 %i.02, 1 %exitcond = icmp eq i32 %inc, 10 br i1 %exitcond, label %for.end, label %for.body for.end: ; preds = %for.body ret i32 0 } ; Function Attrs: nounwind declare i32 @puts(i8* nocapture readonly) #1 指令以下是手册中的描述:

  

在运行时,'phi'指令逻辑上取值   由对应于前任基本块的对指定   在当前块之前执行的。

所以,让我们再次引起我们的注意:

%inc = add nuw nsw i32 %i.02, 1

当我们第一次遇到phi时,我们传递的最后一个标签是for.body: %i.02 = phi i32 [ 0, %entry ], [ %inc, %for.body ] %puts = tail call i32 @puts(i8* getelementptr inbounds ([6 x i8]* @str, i32 0, i32 0)) %inc = add nuw nsw i32 %i.02, 1 %exitcond = icmp eq i32 %inc, 10 br i1 %exitcond, label %for.end, label %for.body 。因此,我们被指示将%i.02设置为0.即使我们已经传递了entry标签,执行命令的最后一个前导块也在%i.02块中,使{ {1}}最后一个标签。接下来,我们调用控制台打印功能。然后,我们声明一个变量for.body并将其设置为我们的'i'变量+ 1,并且这是第一个增量,它将变为0.最后,我们有一个布尔比较检查我们的'i'值小于10,在这种情况下,entry指令将我们发送回顶部。现在是棘手的部分:entry可以告诉代码最后在%inc标签中执行。这意味着我们仍然在br前任区块中,phi仍在我们的符号表中,这就是我们可以将for.body设置为for.body的原因。 %inc将继续递增,直到它等于10,%i.02指令将跳转到%inc标签,从而退出循环。

有关上述所有说明的详细信息,请访问LLVM Language Reference Manual

答案 1 :(得分:0)

LLVM IR中没有循环概念,您只有标签和分支。

将0到10之间的数字加到变量acc中的循环可以写成三个基本块:

%L0: ; loop init
  ; init i to 0 and acc to 0
  br label %L1
%L1: ; loop condition
  ; test if i <= 10, and store result in %cond
  br i1 %cond, label %L2, label %L3
%L2: ; loop body
  ; perform acc += i and i++
  br %L1
%L3: ; loop exit point
  ; the rest of the program
  ; ...

请注意,br指令有两种使用方式,它是标签的直接分支或两个标签之间的条件分支,具体取决于布尔值(LLVM用语中为i1)。 / p>

因此,从IR获取循环语义需要像LoopInfo那样的分析传递。