【发布时间】:2015-02-16 21:31:29
【问题描述】:
如何在 LLVM IR 中将 do-while 形式的循环更改为 while-form 的循环?
【问题讨论】:
-
您想在 clang 的 AST 上执行此操作还是在 IR 中执行此操作?
-
我想在 IR 中这样做。
标签: llvm llvm-clang llvm-ir
如何在 LLVM IR 中将 do-while 形式的循环更改为 while-form 的循环?
【问题讨论】:
标签: llvm llvm-clang llvm-ir
这里有一个小循环示例。这些循环只是在一个布尔数组中运行,直到它们找到第一次出现的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 循环,首先评估条件。这很容易。现在您有两种选择如何处理这种情况。您可以尝试修改条件,这是一项非常艰巨的任务,因为您可以将几乎所有内容都放在循环条件中。简单的方法是在循环的第一个分支之前复制一次循环体(从;<label>:4 到;<label>: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);
我希望这有点帮助。玩得开心!
【讨论】: