如何在LLVM IR中将do-while表单循环更改为while表单循环

时间:2014-12-18 06:53:57

标签: llvm llvm-clang llvm-ir

如何将do-while形式的循环更改为LLVM IR中的while-form循环?

1 个答案:

答案 0 :(得分:2)

这里有一个小循环示例。循环只是在布尔数组中运行,直到找到第一次出现的true。我用clang -emit-llvm编译它以获得优化的llvm IR。

#include <stdio.h>
#include <string.h>

int foo(bool* start){
        bool* cond = start;;
        while (*cond != true)
                cond++;
        return cond - start;
}

int bar(bool* start){
        bool* cond = start;
        do {
        }while (*(++cond) != true);
        return cond - start;
}

int main(){
        bool cond[8];
        memset(&cond, 0, sizeof(bool)*8);
        cond[5] = true;
        printf("%i %i\n", foo(cond), bar(cond));
}

foo函数的IR(仅使用while循环)如下所示:

; Function Attrs: nounwind uwtable
define i32 @_Z3fooPb(i8* %start) #0 {
  %1 = alloca i8*, align 8
  %cond = alloca i8*, align 8
  store i8* %start, i8** %1, align 8
  %2 = load i8** %1, align 8
  store i8* %2, i8** %cond, align 8
  br label %3

; <label>:3                                       ; preds = %9, %0
  %4 = load i8** %cond, align 8
  %5 = load i8* %4, align 1
  %6 = trunc i8 %5 to i1
  %7 = zext i1 %6 to i32
  %8 = icmp ne i32 %7, 1
  br i1 %8, label %9, label %12

; <label>:9                                       ; preds = %3
  %10 = load i8** %cond, align 8
  %11 = getelementptr inbounds i8* %10, i32 1
  store i8* %11, i8** %cond, align 8
  br label %3

; <label>:12                                      ; preds = %3
  %13 = load i8** %cond, align 8
  %14 = load i8** %1, align 8
  %15 = ptrtoint i8* %13 to i64
  %16 = ptrtoint i8* %14 to i64
  %17 = sub i64 %15, %16
  %18 = trunc i64 %17 to i32
  ret i32 %18
}

和bar,我们得到的时候使用do:

; Function Attrs: nounwind uwtable
define i32 @_Z3barPb(i8* %start) #0 {
  %1 = alloca i8*, align 8
  %cond = alloca i8*, align 8
  store i8* %start, i8** %1, align 8
  %2 = load i8** %1, align 8
  store i8* %2, i8** %cond, align 8
  br label %3

; <label>:3                                       ; preds = %4, %0
  br label %4

; <label>:4                                       ; preds = %3
  %5 = load i8** %cond, align 8
  %6 = getelementptr inbounds i8* %5, i32 1
  store i8* %6, i8** %cond, align 8
  %7 = load i8* %6, align 1
  %8 = trunc i8 %7 to i1
  %9 = zext i1 %8 to i32
  %10 = icmp ne i32 %9, 1
  br i1 %10, label %3, label %11

; <label>:11                                      ; preds = %4
  %12 = load i8** %cond, align 8
  %13 = load i8** %1, align 8
  %14 = ptrtoint i8* %12 to i64
  %15 = ptrtoint i8* %13 to i64
  %16 = sub i64 %14, %15
  %17 = trunc i64 %16 to i32
  ret i32 %17
}

bar的差异非常小我们有一个额外的标签和一个额外的br因为我们在评估条件之前跳转到循环体并执行它。

因此,转换do的第一件事是摆脱分支并跳转到条件。现在是一个while循环,首先评估条件。这很容易。现在,您有两种选择来处理这种情况。您可以尝试修改条件什么是真正的硬任务,因为您可以将几乎所有内容放在循环条件中。简单的方法是在循环的第一个分支之前复制循环体一次(从;<label>:4;<label>:11的所有内容)。所以你想要改变代码的正确性,你的do-while循环将成为循环前面的循环(循环体执行)。

您可以使用CloneBasicBlock

中的llvm/Transforms/Utils/Cloning.h复制循环体
/// CloneBasicBlock - Return a copy of the specified basic block, but without
/// embedding the block into a particular function.  The block returned is an
/// exact copy of the specified basic block, without any remapping having been
/// performed.  Because of this, this is only suitable for applications where
/// the basic block will be inserted into the same function that it was cloned
/// from (loop unrolling would use this, for example).
///
/// Also, note that this function makes a direct copy of the basic block, and
/// can thus produce illegal LLVM code.  In particular, it will copy any PHI
/// nodes from the original block, even though there are no predecessors for the
/// newly cloned block (thus, phi nodes will have to be updated).  Also, this
/// block will branch to the old successors of the original block: these
/// successors will have to have any PHI nodes updated to account for the new
/// incoming edges.
///
/// The correlation between instructions in the source and result basic blocks
/// is recorded in the VMap map.
///
/// If you have a particular suffix you'd like to use to add to any cloned
/// names, specify it as the optional third parameter.
///
/// If you would like the basic block to be auto-inserted into the end of a
/// function, you can specify it as the optional fourth parameter.
///
/// If you would like to collect additional information about the cloned
/// function, you can specify a ClonedCodeInfo object with the optional fifth
/// parameter.
///
BasicBlock *CloneBasicBlock(const BasicBlock *BB,
                            ValueToValueMapTy &VMap,
                            const Twine &NameSuffix = "", Function *F = nullptr,
                            ClonedCodeInfo *CodeInfo = nullptr);

我希望这有点帮助。玩得开心!