Compiler construction 如何在LLVM上创建循环对象?
我想知道LLVM如何创建循环对象 有许多与循环相关的对象,如LoopInfo、LoopBase、Loop等 但是我找不到创建这些对象的LLVM源代码的位置 我想知道他们是如何跟踪后缘的,以及如何识别这是一个循环Compiler construction 如何在LLVM上创建循环对象?,compiler-construction,llvm,Compiler Construction,Llvm,我想知道LLVM如何创建循环对象 有许多与循环相关的对象,如LoopInfo、LoopBase、Loop等 但是我找不到创建这些对象的LLVM源代码的位置 我想知道他们是如何跟踪后缘的,以及如何识别这是一个循环 也就是说,我想学习有关在LLVM上检测和分析循环信息的全部原理。在LLVM IR中没有循环的概念,只有标签和分支 将数字从0到10相加为变量acc的循环可以写成三个基本块: %L0: ; loop init ; init i to 0 and acc to 0 br label
也就是说,我想学习有关在LLVM上检测和分析循环信息的全部原理。在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
; ...
%L0:;循环初始化
; 初始i到0和acc到0
br标签%L1
%L1:;循环条件
; 测试是否有两种方法可以实现常规循环。一个不使用phi
指令,但另一个都使用br
运算符
请看下面的代码:
#include <stdio.h>
int main () {
for(int i = 0; i < 10; i++) {
printf("Test.\n");
}
return 0;
}
首先,我们创建标识符来表示i
和返回值变量,然后将它们分别设置为0。它转到第一个标签并开始循环,首先计算由icmp,
指令产生的布尔值,其中slt
=“签名小于”。如果%i
小于10,则将在%cmp
中存储true,否则将存储false。
br
指令后面的br i1,label,label
重载语法定义,如果%cmp
为true,程序将跳转到iftrue
标签,在本例中,%for.body
,否则,它将跳转到iffalse
标签,在本例中,%for.end
。总之,如果%i
仍然小于10,则此指令将进入循环体,如果达到10,则将退出循环。有了br
指令的最终知识,程序其余部分的行为应该是显而易见的
现在,虽然第二种方法要短得多,但它稍微复杂一些。我使用Clang with commandClang-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
时,我们传递的最后一个标签是条目
。因此,我们被指示将%i.02
设置为0。尽管我们已经传递了for.body
标签,但执行命令的最后一个前置块位于entry
块中,使entry
成为最后一个标签。接下来,我们调用控制台打印函数。然后,我们声明一个变量%inc
,并将其设置为'i'变量+1,这是第一个增量,它将变为0。最后,我们有一个布尔比较,检查我们的“i”值是否小于10,在本例中,br
指令将我们发回顶部。现在是棘手的部分:phi
可以判断代码最后一次是在.body
标签中执行的。这意味着我们仍在.body
的前置块中,%inc
仍在符号表中,这就是为什么我们可以将%i.02
设置为%inc
%inc
将继续递增,直到它等于10,br
指令将跳转到.end
标签的%处,从而退出循环
有关所有说明的更多信息,请访问。这并不能回答问题。
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