【问题标题】:LLVM Create VarArg Function and access var argsLLVM 创建 VarArg 函数并访问 var args
【发布时间】:2016-03-08 18:10:37
【问题描述】:

我一直在尝试使用 LLVM 中的模块传递来创建一个函数。我想做的是创建一个可变参数函数,然后添加逻辑来操作可变参数。 例如:

/\*can do this\*/

int foo(int a, ...)
{

    double var1;
    //can't figure out how to add any of this using llvm

    va_list ap;     
    va_start(ap, a);
    va_arg(var1,double);
    va_end(ap);
}

创建函数类型很容易,因为我只是将 vararg 布尔值设置为 true。之后我该怎么办?

【问题讨论】:

    标签: llvm llvm-clang llvm-ir


    【解决方案1】:

    我总是使用 clang 来检查它需要转换为 c/c++ lang 的内容。 使用 llvm 指令 va_arg 和 intinsics llvm.va_start、llvm.va_end、llvm.va_copy 来使用 llvm 变量参数支持。

    对于使用 this 的参数进行操作的函数,您还需要特定于目标的值类型“va_list”。

    ; This struct is different for every platform. For most platforms,
    ; it is merely an i8*.
    %struct.va_list = type { i8* }
    
    ; For Unix x86_64 platforms, va_list is the following struct:
    ; %struct.va_list = type { i32, i32, i8*, i8* }
    

    参考http://llvm.org/docs/LangRef.html#variable-argument-handling-intrinsics

    对于您列出的代码,

        ; ModuleID = 'test.c'
        target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
        target triple = "x86_64-pc-linux-gnu"
    
        %struct.__va_list_tag = type { i32, i32, i8*, i8* }
    
        ; Function Attrs: nounwind uwtable
        define i32 @foo(i32 %a, ...) #0 {
          %1 = alloca i32, align 4
          %2 = alloca i32, align 4
          %var1 = alloca double, align 8
          %ap = alloca [1 x %struct.__va_list_tag], align 16
          store i32 %a, i32* %2, align 4
          %3 = getelementptr inbounds [1 x %struct.__va_list_tag]* %ap, i32 0, i32 0
          %4 = bitcast %struct.__va_list_tag* %3 to i8*
          call void @llvm.va_start(i8* %4)
          %5 = getelementptr inbounds [1 x %struct.__va_list_tag]* %ap, i32 0, i32 0
          %6 = getelementptr inbounds %struct.__va_list_tag* %5, i32 0, i32 1
          %7 = load i32* %6
          %8 = icmp ule i32 %7, 160
          br i1 %8, label %9, label %15
    
        ; <label>:9                                       ; preds = %0
          %10 = getelementptr inbounds %struct.__va_list_tag* %5, i32 0, i32 3
          %11 = load i8** %10
          %12 = getelementptr i8* %11, i32 %7
          %13 = bitcast i8* %12 to double*
          %14 = add i32 %7, 16
          store i32 %14, i32* %6
          br label %20
    
        ; <label>:15                                      ; preds = %0
          %16 = getelementptr inbounds %struct.__va_list_tag* %5, i32 0, i32 2
          %17 = load i8** %16
          %18 = bitcast i8* %17 to double*
          %19 = getelementptr i8* %17, i32 8
          store i8* %19, i8** %16
          br label %20
    
        ; <label>:20                                      ; preds = %15, %9
          %21 = phi double* [ %13, %9 ], [ %18, %15 ]
          %22 = load double* %21
          %23 = getelementptr inbounds [1 x %struct.__va_list_tag]* %ap, i32 0, i32 0
          %24 = bitcast %struct.__va_list_tag* %23 to i8*
          call void @llvm.va_end(i8* %24)
          %25 = load i32* %1
          ret i32 %25
        }
    
        ; Function Attrs: nounwind
        declare void @llvm.va_start(i8*) #1
    
        ; Function Attrs: nounwind
        declare void @llvm.va_end(i8*) #1
    
        ; Function Attrs: nounwind uwtable
        define i32 @main() #0 {
          ret i32 0
        }
    

    【讨论】:

    • 当我在 IR Builder 中使用 Module pass 时,如何添加 llvm.a_start?例如,如果我想进行分配,那么我调用 builder.Alloca()。我没有看到 va_start 或 end 类似的东西
    • va_start、va_end 和 va_copy 是 llvm 内在函数,要添加它们的调用,您可以使用 sIRB.CreateCall(Intrinsic::getDeclaration(currentModule, llvm::Intrinsic::va_start, ArrayRef( Tys,numTys)), args),这将添加声明和创建调用。 PS您需要在getDeclaration时检查内在是否重载。
    • 请参阅 stackoverflow.com/questions/27681500/… 了解更多信息。
    • 感谢您的帮助。但是由于某种原因,当我尝试创建 va_start 调用指令时,我崩溃了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-31
    • 1970-01-01
    • 2017-11-04
    • 2013-04-17
    • 1970-01-01
    相关资源
    最近更新 更多