[BUG] Varargs example in LangRef segfaults

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

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.

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?

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.