2016-03-08 4 views
1

Я пытался создать функцию, используя пропуск модуля в LLVM. То, что я пытаюсь сделать, это создать переменную функцию аргумента, а затем добавить логику для манипуляции аргументами переменной. Например:LLVM Создать VarArg Функция и доступ var args

/\*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 логическое значение ИСТИНА. Что мне делать после этого?

ответ

2

Я всегда использую clang, чтобы проверить, что ему нужно для преобразования для c/C++ lang. Используйте команду llvm va_arg и intinsics llvm.va_start, llvm.va_end, llvm.va_copy, чтобы использовать поддержку переменных аргументов llvm.

Для функции, которая работает с аргументами, которые используют это значение, вам также нужен тип значения для цели «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 
    } 
+0

Когда я использую пасс модуля с ИК-Builder, как я добавить llvm.a_start? Например, если я хочу выполнить выделение, я вызываю builder.Alloca(). Я не вижу ничего подобного для va_start или end – teslik

+0

va_start, va_end и va_copy - это llvm intrinsics, чтобы добавить их вызов, вы можете использовать sIRB.CreateCall (Intrinsic :: getDeclaration (currentModule, llvm :: Intrinsic :: va_start, ArrayRef (Tys, numTys)), args), это добавит объявление и вызовет вызов. P.S вам нужно проверить, не перегружена ли внутренняя или нет во время getDeclaration. –

+0

см. Http://stackoverflow.com/questions/27681500/generate-call-to-intrinsic-using-llvm-c-api для уточнения. –