我想知道LLVM如何创建Loop对象。
有许多与Loop相关的对象,如LoopInfo,LoopBase,Loop等。
但是我找不到LLVM源代码在创建这些对象的位置。
我想知道他们如何跟踪后沿,以及如何识别这是一个循环。
可以这么说,我想学习有关在LLVM上检测和分析循环信息的完整原则
答案 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
那样的分析传递。