我想知道LLVM如何创建Loop对象。
与Loop相关的对象很多,例如LoopInfo,LoopBase,Loop等。
但是我找不到他们创建这些对象的LLVM源代码的位置。
我想知道他们如何跟踪后端,以及如何识别这是一个循环。
可以这么说,我想学习有关在LLVM上检测和分析Loop信息的整个原理
您可以通过两种方式实现常规循环。一个不使用phi
指令,一个使用,但是都使用br
运算符。
看下面的代码:
#includeint main () { for(int i = 0; i < 10; i++) { printf("Test.\n"); } return 0; }
在这里,我生成了一个使用Clang与command的示例clang -S loop.c -emit-llvm
,使它实现第一个选项:
@.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。它转到第一个标签并开始循环,首先计算icmp
指令产生的布尔值,其中slt
=“小于符号”。如果%i
小于10,则将true存储在中%cmp
,否则,将存储false。br
紧随其后的指令的语法br i1
定义了,如果%cmp
为true,则iftrue
在这种情况下程序将跳转到标签%for.body
,否则,将跳转到iffalse
标签%for.end
。总结一下,如果%i
仍然小于10,如果达到10,它将退出循环。有了br
指令的最终知识,该程序其余部分的行为就应该显而易见。
现在,虽然第二种方法要短得多,但稍微复杂一些。我使用Clang使用command生成了此代码clang -S loop.c -emit-llvm -O1
,即在1级优化标志上进行标记,导致其实现第二个选项:
@.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
忽略增量技术的有趣语法%inc = add nuw nsw i32 %i.02, 1
,它只是整数数学溢出错误处理。我们专注于phi
说明这是手册中的描述:
在运行时,“ 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
,我们通过的最后一个标签是entry
。因此,指示我们将其设置%i.02
为0。即使我们已经传递了for.body
标签,执行命令的最后一个前置程序块仍位于该entry
块中,成为entry
最后一个标签。接下来,我们调用控制台打印功能。然后,我们声明一个变量%inc
并将其设置为'i'变量+ 1,并且这是第一个增量,它将变为0。最后,我们进行了布尔比较,检查我们的'i'值是否小于10,在这种情况下,br
指令会将我们发送回顶部。现在是棘手的部分:phi
可以告诉代码最后在for.body
标签中执行了。这意味着我们仍在for.body
前一个块,%inc
并且仍在我们的符号表中,这就是为什么我们可以设置%i.02
为的原因%inc
。%inc
将继续递增,直到等于10,br
指令将跳转到该%for.end
标号处,从而退出循环。
有关上述所有说明的更多信息,请访问《LLVM语言参考手册》。