[LLVMdev] [BUG] Varargs example in LangRef segfaults

79 views
Skip to first unread message

Ramkumar Ramachandra

unread,
Aug 26, 2014, 3:42:10 PM8/26/14
to LLVMdev
Hi,

So the Variable Argument Handling Intrinsics section of the LangRef
(http://llvm.org/docs/LangRef.html#variable-argument-handling-intrinsics)
lists an example that segfaults. Try the following on x86_64:

-- 8< --
define i32 @test(i32 %X, ...) {
; Initialize variable argument processing
%ap = alloca i8*
%ap2 = bitcast i8** %ap to i8*
call void @llvm.va_start(i8* %ap2)

; Read a single integer argument
%tmp = va_arg i8** %ap, i32

; Demonstrate usage of llvm.va_copy and llvm.va_end
%aq = alloca i8*
%aq2 = bitcast i8** %aq to i8*
call void @llvm.va_copy(i8* %aq2, i8* %ap2)
call void @llvm.va_end(i8* %aq2)

; Stop processing of arguments.
call void @llvm.va_end(i8* %ap2)
ret i32 %tmp
}

define i32 @main() {
%call = call i32 (i32, ...)* @test(i32 1, i32 3)
ret i32 %call
}

declare void @llvm.va_start(i8*)
declare void @llvm.va_copy(i8*, i8*)
declare void @llvm.va_end(i8*)
-- 8< --

It happens because va_arg is apparently not implemented properly on
X86 (I saw tests/Codegen/X86/vaargs.ll). What should be done about the
situation?
1. Update the LangRef.
2. Fix va_arg for x86.

If (2) is the way to go, I'll take a stab at it.

Thanks.

Ram
_______________________________________________
LLVM Developers mailing list
LLV...@cs.uiuc.edu http://llvm.cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev

Reid Kleckner

unread,
Aug 26, 2014, 4:33:47 PM8/26/14
to Ramkumar Ramachandra, LLVMdev
The problem is that the type of the va_list is platform-specific. No IR example will work on all platforms, unless you overallocate. The LangRef should probably be updated to use the SysV x86_64 va_list as that is the most common and mention in an IR comment that the type depends on the platform.

Ramkumar Ramachandra

unread,
Aug 26, 2014, 5:40:06 PM8/26/14
to Reid Kleckner, LLVMdev
So I've written out this sample based on what Clang emitted:

-- 8< --
%struct.__va_list_tag = type { i32, i32, i8*, i8* }

define i32 @test(i32 %X, ...) {
; Initialize variable argument processing
%ap = alloca %struct.__va_list_tag
%ap2 = bitcast %struct.__va_list_tag* %ap to i8*
%retptr = alloca i32
call void @llvm.va_start(i8* %ap2)

; Read a single integer argument
%idxptr = getelementptr inbounds %struct.__va_list_tag* %ap, i32 0, i32 0
%idx = load i32* %idxptr
%tmp = getelementptr inbounds %struct.__va_list_tag* %ap, i32 0, i32 3
%extract = load i8** %tmp
%rawel = getelementptr i8* %extract, i32 %idx
%elptr = bitcast i8* %rawel to i32*
%newidx = add i32 %idx, 8
store i32 %idx, i32* %idxptr

; Store that argument in el
%el = load i32* %elptr
store i32 %el, i32* %retptr
%ret = load i32* %retptr

; Stop processing of arguments.
call void @llvm.va_end(i8* %ap2)
ret i32 %ret
}

define i32 @main() {
%call = call i32 (i32, ...)* @test(i32 1, i32 3)
ret i32 %call
}

declare void @llvm.va_start(i8*)
declare void @llvm.va_copy(i8*, i8*)
declare void @llvm.va_end(i8*)
-- 8< --

Can we include (a neater version of) this in the LangRef? Can it not
be simplified?

I understand that %struct.__va_list_tag will be different for each
platform, so the user can't gep into the structure in a
platform-independent manner, but what's the problem with this?

%ap = alloca_va_list
%ap2 = bitcast %struct.__va_list_tag* %ap to i8*
call void @llvm.va_start(i8* %ap2)
va_arg %ap i32
call void @llvm.va_end(i8* %ap2)

All we need is a sort of global %struct.__va_list_tag, right?

Reid Kleckner

unread,
Aug 26, 2014, 6:00:33 PM8/26/14
to Ramkumar Ramachandra, LLVMdev
va_arg of struct types is not implemented in the x86_64 backend:

  // Decide which area this value should be read from.
  // TODO: Implement the AMD64 ABI in its entirety. This simple
  // selection mechanism works only for the basic types.
  if (ArgVT == MVT::f80) {
    llvm_unreachable("va_arg for f80 not yet implemented");
  } else if (ArgVT.isFloatingPoint() && ArgSize <= 16 /*bytes*/) {
    ArgMode = 2;  // Argument passed in XMM register. Use fp_offset.
  } else if (ArgVT.isInteger() && ArgSize <= 32 /*bytes*/) {
    ArgMode = 1;  // Argument passed in GPR64 register(s). Use gp_offset.
  } else {
    llvm_unreachable("Unhandled argument type in LowerVAARG");
  }

So Clang lowers it manually.

If you only use builtin types such as integers, pointers, floats, and doubles, then va_arg works.
Reply all
Reply to author
Forward
0 new messages