【问题标题】:How to change a do-while form loop into a while form loop in LLVM IR如何在 LLVM IR 中将 do-while 表单循环更改为 while 表单循环
【发布时间】:2015-02-16 21:31:29
【问题描述】:

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

【问题讨论】:

  • 您想在 clang 的 AST 上执行此操作还是在 IR 中执行此操作?
  • 我想在 IR 中这样做。

标签: llvm llvm-clang llvm-ir


【解决方案1】:

这里有一个小循环示例。这些循环只是在一个布尔数组中运行,直到它们找到第一次出现的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 的第一件事就是摆脱分支并跳转到条件。现在它是一个 while 循环,首先评估条件。这很容易。现在您有两种选择如何处理这种情况。您可以尝试修改条件,这是一项非常艰巨的任务,因为您可以将几乎所有内容都放在循环条件中。简单的方法是在循环的第一个分支之前复制一次循环体(从;&lt;label&gt;:4;&lt;label&gt;:11)。所以你想改变你的代码的正确性,你的 do-while 循环将成为循环前面的一个循环(在循环体上执行)。

你可以从llvm/Transforms/Utils/Cloning.h复制带有CloneBasicBlock的循环体:

/// 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);

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

【讨论】:

  • 感谢您的回答。如果循环有多个条件,这将是一项复杂的工作,因为 llvm 无法回答 BasicBlock 是否属于循环条件。在这种情况下,在 C/C++ 源代码上执行此操作会更容易。我以为clang或llvm中会有API可以做到这一点。
猜你喜欢
  • 2020-08-28
  • 1970-01-01
  • 2015-02-16
  • 1970-01-01
  • 2021-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多