【问题标题】:LLVM intrinsic functionsLLVM 内在函数
【发布时间】:2014-12-16 18:55:14
【问题描述】:

使用 LLVM 构建项目时,一些函数调用将被内部函数替换。替换是由前端(例如clang)还是LLVM后端完成的?

互联网上的讨论表明,内在函数替换与优化选项有关。那么这是否意味着如果没有优化选项,那么就不会发生内在替换?或者实际上有一些不能禁用的默认内部函数替换?

如果有任何方法可以禁用所有内在函数,我应该怎么做?

【问题讨论】:

    标签: llvm


    【解决方案1】:

    这取决于。用代码编写的内部函数直接通过前端发出。像 llvm.memset 这样的内在函数在 IR 级别的优化期间被引入代码(无论是前端还是后端都执行此优化)。

    这是一个(相当愚蠢的)例子:

    int main(int argc, char** argv)
    {
            int a[8];
    
            for (int i = 0; i != 8; ++i)
                    a[i] = 0;
    
            for (int i = 7; i >= 0; --i)
                    a[i] = a[i+1] + argc;
    
            return a[0];
    }
    

    使用 clang 3.5 (clang -S -emit-llvm) 编译,您将获得以下不带任何内在函数的 IR:

    ; Function Attrs: nounwind uwtable
    define i32 @main(i32 %argc, i8** %argv) #0 {
      %1 = alloca i32, align 4
      %2 = alloca i32, align 4
      %3 = alloca i8**, align 8
      %a = alloca [8 x i32], align 16
      %i = alloca i32, align 4
      %i1 = alloca i32, align 4
      store i32 0, i32* %1
      store i32 %argc, i32* %2, align 4
      store i8** %argv, i8*** %3, align 8
      store i32 0, i32* %i, align 4
      br label %4
    
    ; <label>:4                                       ; preds = %11, %0
      %5 = load i32* %i, align 4
      %6 = icmp ne i32 %5, 8
      br i1 %6, label %7, label %14
    
    ; <label>:7                                       ; preds = %4
      %8 = load i32* %i, align 4
      %9 = sext i32 %8 to i64
      %10 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 %9
      store i32 0, i32* %10, align 4
      br label %11
    
    ; <label>:11                                      ; preds = %7
      %12 = load i32* %i, align 4
      %13 = add nsw i32 %12, 1
      store i32 %13, i32* %i, align 4
      br label %4
    
    ; <label>:14                                      ; preds = %4
      store i32 7, i32* %i1, align 4
      br label %15
    
    ; <label>:15                                      ; preds = %29, %14
      %16 = load i32* %i1, align 4
      %17 = icmp sge i32 %16, 0
      br i1 %17, label %18, label %32
    
    ; <label>:18                                      ; preds = %15
      %19 = load i32* %i1, align 4
      %20 = add nsw i32 %19, 1
      %21 = sext i32 %20 to i64
      %22 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 %21
      %23 = load i32* %22, align 4
      %24 = load i32* %2, align 4
      %25 = add nsw i32 %23, %24
      %26 = load i32* %i1, align 4
      %27 = sext i32 %26 to i64
      %28 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 %27
      store i32 %25, i32* %28, align 4
      br label %29
    
    ; <label>:29                                      ; preds = %18
      %30 = load i32* %i1, align 4
      %31 = add nsw i32 %30, -1
      store i32 %31, i32* %i1, align 4
      br label %15
    
    ; <label>:32                                      ; preds = %15
      %33 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 0
      %34 = load i32* %33, align 4
      ret i32 %34
    }
    

    clang -emit-llvm -O1再次编译你会看到这个:

    ; Function Attrs: nounwind readnone uwtable
    define i32 @main(i32 %argc, i8** nocapture readnone %argv) #0 {
    .preheader:
      %a = alloca [8 x i32], align 16
      %a6 = bitcast [8 x i32]* %a to i8*
      call void @llvm.memset.p0i8.i64(i8* %a6, i8 0, i64 32, i32 4, i1 false)
      br label %0
    
    ; <label>:0                                       ; preds = %.preheader, %0
      %indvars.iv = phi i64 [ 7, %.preheader ], [ %indvars.iv.next, %0 ]
      %1 = add nsw i64 %indvars.iv, 1
      %2 = getelementptr inbounds [8 x i32]* %a, i64 0, i64 %1
      %3 = load i32* %2, align 4, !tbaa !1
      %4 = add nsw i32 %3, %argc
      %5 = getelementptr inbounds [8 x i32]* %a, i64 0, i64 %indvars.iv
      store i32 %4, i32* %5, align 4, !tbaa !1
      %indvars.iv.next = add nsw i64 %indvars.iv, -1
      %6 = trunc i64 %indvars.iv to i32
      %7 = icmp sgt i32 %6, 0
      br i1 %7, label %0, label %8
    
    ; <label>:8                                       ; preds = %0
      %9 = getelementptr inbounds [8 x i32]* %a, i64 0, i64 0
      %10 = load i32* %9, align 16, !tbaa !1
      ret i32 %10
    }
    

    初始化循环被 llvm.memset 内部函数替换。后端可以随意处理内在函数,但通常 llvm.memset 会降低为 libc 库调用。

    回答您的第一个问题:是的,如果您不优化代码,那么您将不会在 IR 中获得内在函数。

    为了防止在您的代码中引入内在函数,您所要做的就是在您的 IR 上找到优化传递并且不要运行它。这是一个相关的问题,如何找出在 IR 上完成了哪些通行证:Where to find the optimization sequence for clang -OX?

    对于-O1,我们得到:

    修剪-eh -inline-cost -always-inline -functionattrs -sroa -domtree -early-cse -lazy-value-info -jump-threading -correlated-propagation -simplifycfg -instcombine -tailcallelim -simplifycfg -reassociate -domtree -loops -loop-simplify -lcssa -loop-rotate -licm -loop-unswitch -instcombine -scalar-evolution -lcssa -indvars -loop-idiom -loop-deletion -loop-unroll -memdep -memcpyopt -sccp -instcombine -lazy-value-info -jump-threading -correlated-propagation -domtree -memdep -dse -adce -simplifycfg -instcombine -barrier -domtree -loops -loop-simplify -lcssa -branch-prob -block-freq -scalar-evolution -loop-vectorize -instcombine -simplifycfg -strip-dead-prototypes -verify

    一个大胆的猜测:instcombine 正在引入 llvm.memset。我在没有 instcombine 的情况下运行通行证并选择未优化的 IR 并得到这个:

    ; Function Attrs: nounwind readnone uwtable
    define i32 @main(i32 %argc, i8** %argv) #0 {
      %a = alloca [8 x i32], align 16
      %1 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 8
      %2 = load i32* %1, align 4
      %3 = add nsw i32 %2, %argc
      %4 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 7
      store i32 %3, i32* %4, align 4
      %5 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 7
      %6 = load i32* %5, align 4
      %7 = add nsw i32 %6, %argc
      %8 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 6
      store i32 %7, i32* %8, align 4
      %9 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 6
      %10 = load i32* %9, align 4
      %11 = add nsw i32 %10, %argc
      %12 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 5
      store i32 %11, i32* %12, align 4
      %13 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 5
      %14 = load i32* %13, align 4
      %15 = add nsw i32 %14, %argc
      %16 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 4
      store i32 %15, i32* %16, align 4
      %17 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 4
      %18 = load i32* %17, align 4
      %19 = add nsw i32 %18, %argc
      %20 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 3
      store i32 %19, i32* %20, align 4
      %21 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 3
      %22 = load i32* %21, align 4
      %23 = add nsw i32 %22, %argc
      %24 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 2
      store i32 %23, i32* %24, align 4
      %25 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 2
      %26 = load i32* %25, align 4
      %27 = add nsw i32 %26, %argc
      %28 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 1
      store i32 %27, i32* %28, align 4
      %29 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 1
      %30 = load i32* %29, align 4
      %31 = add nsw i32 %30, %argc
      %32 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 0
      store i32 %31, i32* %32, align 4
      %33 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 0
      %34 = load i32* %33, align 4
      ret i32 %34
    }
    

    没有说明。因此,为了防止(至少是 memset)代码中的内在函数,请不要在 IR 上运行 instcombine。然而,instcombine 是一个强大的选择通道,它真正缩短了代码。

    现在你有两个选择:

    1. 不要使用引入内在函数的 opt pass
    2. 编写自己的 llvm opt pass 将内在函数转换回它们可能的状态 替换为在优化之后和后端之前运行它 开始工作

    我希望这对您有所帮助。干杯!

    【讨论】:

    • clang 5 即使使用 -O0 也会生成内在函数
    • @TongZhou 有一个来自 clang 5+ 的内在示例会很棒
    猜你喜欢
    • 2015-05-13
    • 2012-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-05
    • 2011-02-11
    • 2013-12-31
    • 2014-10-07
    相关资源
    最近更新 更多