Compiler construction 如何在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如何创建循环对象

有许多与循环相关的对象,如LoopInfo、LoopBase、Loop等

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

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


也就是说,我想学习有关在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 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
时,我们传递的最后一个标签是
条目
。因此,我们被指示将
%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