Support __builtin_ms_va_list.

Summary:
This change adds support for `__builtin_ms_va_list`, a GCC extension for
variadic `ms_abi` functions. The existing `__builtin_va_list` support is
inadequate for this because `va_list` is defined differently in the Win64
ABI vs. the System V/AMD64 ABI.

Depends on D1622.

Reviewers: rsmith, rnk, rjmccall

CC: cfe-commits

Differential Revision: http://reviews.llvm.org/D1623

llvm-svn: 247941
diff --git a/clang/test/CodeGen/ms_abi.c b/clang/test/CodeGen/ms_abi.c
index 7c5c26f..2cca249 100644
--- a/clang/test/CodeGen/ms_abi.c
+++ b/clang/test/CodeGen/ms_abi.c
@@ -1,20 +1,145 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-freebsd10.0 -emit-llvm < %s | FileCheck -check-prefix=FREEBSD %s
 // RUN: %clang_cc1 -triple x86_64-pc-win32 -emit-llvm < %s | FileCheck -check-prefix=WIN64 %s
 
+struct foo {
+  int x;
+  float y;
+  char z;
+};
+// FREEBSD: %[[STRUCT_FOO:.*]] = type { i32, float, i8 }
+// WIN64: %[[STRUCT_FOO:.*]] = type { i32, float, i8 }
+
 void __attribute__((ms_abi)) f1(void);
 void __attribute__((sysv_abi)) f2(void);
 void f3(void) {
-// FREEBSD: define void @f3()
-// WIN64: define void @f3()
+  // FREEBSD-LABEL: define void @f3()
+  // WIN64-LABEL: define void @f3()
   f1();
-// FREEBSD: call x86_64_win64cc void @f1()
-// WIN64: call void @f1()
+  // FREEBSD: call x86_64_win64cc void @f1()
+  // WIN64: call void @f1()
   f2();
-// FREEBSD: call void @f2()
-// WIN64: call x86_64_sysvcc void @f2()
+  // FREEBSD: call void @f2()
+  // WIN64: call x86_64_sysvcc void @f2()
 }
 // FREEBSD: declare x86_64_win64cc void @f1()
 // FREEBSD: declare void @f2()
 // WIN64: declare void @f1()
 // WIN64: declare x86_64_sysvcc void @f2()
 
+// Win64 ABI varargs
+void __attribute__((ms_abi)) f4(int a, ...) {
+  // FREEBSD-LABEL: define x86_64_win64cc void @f4
+  // WIN64-LABEL: define void @f4
+  __builtin_ms_va_list ap;
+  __builtin_ms_va_start(ap, a);
+  // FREEBSD: %[[AP:.*]] = alloca i8*
+  // FREEBSD: call void @llvm.va_start
+  // WIN64: %[[AP:.*]] = alloca i8*
+  // WIN64: call void @llvm.va_start
+  int b = __builtin_va_arg(ap, int);
+  // FREEBSD: %[[AP_CUR:.*]] = load i8*, i8** %[[AP]]
+  // FREEBSD-NEXT: %[[AP_NEXT:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR]], i64 8
+  // FREEBSD-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]]
+  // FREEBSD-NEXT: bitcast i8* %[[AP_CUR]] to i32*
+  // WIN64: %[[AP_CUR:.*]] = load i8*, i8** %[[AP]]
+  // WIN64-NEXT: %[[AP_NEXT:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR]], i64 8
+  // WIN64-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]]
+  // WIN64-NEXT: bitcast i8* %[[AP_CUR]] to i32*
+  double _Complex c = __builtin_va_arg(ap, double _Complex);
+  // FREEBSD: %[[AP_CUR2:.*]] = load i8*, i8** %[[AP]]
+  // FREEBSD-NEXT: %[[AP_NEXT2:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR2]], i64 16
+  // FREEBSD-NEXT: store i8* %[[AP_NEXT2]], i8** %[[AP]]
+  // FREEBSD-NEXT: bitcast i8* %[[AP_CUR2]] to { double, double }*
+  // WIN64: %[[AP_CUR2:.*]] = load i8*, i8** %[[AP]]
+  // WIN64-NEXT: %[[AP_NEXT2:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR2]], i64 16
+  // WIN64-NEXT: store i8* %[[AP_NEXT2]], i8** %[[AP]]
+  // WIN64-NEXT: bitcast i8* %[[AP_CUR2]] to { double, double }*
+  struct foo d = __builtin_va_arg(ap, struct foo);
+  // FREEBSD: %[[AP_CUR3:.*]] = load i8*, i8** %[[AP]]
+  // FREEBSD-NEXT: %[[AP_NEXT3:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR3]], i64 16
+  // FREEBSD-NEXT: store i8* %[[AP_NEXT3]], i8** %[[AP]]
+  // FREEBSD-NEXT: bitcast i8* %[[AP_CUR3]] to %[[STRUCT_FOO]]*
+  // WIN64: %[[AP_CUR3:.*]] = load i8*, i8** %[[AP]]
+  // WIN64-NEXT: %[[AP_NEXT3:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR3]], i64 16
+  // WIN64-NEXT: store i8* %[[AP_NEXT3]], i8** %[[AP]]
+  // WIN64-NEXT: bitcast i8* %[[AP_CUR3]] to %[[STRUCT_FOO]]*
+  __builtin_ms_va_list ap2;
+  __builtin_ms_va_copy(ap2, ap);
+  // FREEBSD: %[[AP_VAL:.*]] = load i8*, i8** %[[AP]]
+  // FREEBSD-NEXT: store i8* %[[AP_VAL]], i8** %[[AP2:.*]]
+  // WIN64: %[[AP_VAL:.*]] = load i8*, i8** %[[AP]]
+  // WIN64-NEXT: store i8* %[[AP_VAL]], i8** %[[AP2:.*]]
+  __builtin_ms_va_end(ap);
+  // FREEBSD: call void @llvm.va_end
+  // WIN64: call void @llvm.va_end
+}
+
+// Let's verify that normal va_lists work right on Win64, too.
+void f5(int a, ...) {
+  // WIN64-LABEL: define void @f5
+  __builtin_va_list ap;
+  __builtin_va_start(ap, a);
+  // WIN64: %[[AP:.*]] = alloca i8*
+  // WIN64: call void @llvm.va_start
+  int b = __builtin_va_arg(ap, int);
+  // WIN64: %[[AP_CUR:.*]] = load i8*, i8** %[[AP]]
+  // WIN64-NEXT: %[[AP_NEXT:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR]], i64 8
+  // WIN64-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]]
+  // WIN64-NEXT: bitcast i8* %[[AP_CUR]] to i32*
+  double _Complex c = __builtin_va_arg(ap, double _Complex);
+  // WIN64: %[[AP_CUR2:.*]] = load i8*, i8** %[[AP]]
+  // WIN64-NEXT: %[[AP_NEXT2:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR2]], i64 16
+  // WIN64-NEXT: store i8* %[[AP_NEXT2]], i8** %[[AP]]
+  // WIN64-NEXT: bitcast i8* %[[AP_CUR2]] to { double, double }*
+  struct foo d = __builtin_va_arg(ap, struct foo);
+  // WIN64: %[[AP_CUR3:.*]] = load i8*, i8** %[[AP]]
+  // WIN64-NEXT: %[[AP_NEXT3:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR3]], i64 16
+  // WIN64-NEXT: store i8* %[[AP_NEXT3]], i8** %[[AP]]
+  // WIN64-NEXT: bitcast i8* %[[AP_CUR3]] to %[[STRUCT_FOO]]*
+  __builtin_va_list ap2;
+  __builtin_va_copy(ap2, ap);
+  // WIN64: call void @llvm.va_copy
+  __builtin_va_end(ap);
+  // WIN64: call void @llvm.va_end
+}
+
+// Verify that using a Win64 va_list from a System V function works.
+void __attribute__((sysv_abi)) f6(__builtin_ms_va_list ap) {
+  // FREEBSD-LABEL: define void @f6
+  // FREEBSD: store i8* %ap, i8** %[[AP:.*]]
+  // WIN64-LABEL: define x86_64_sysvcc void @f6
+  // WIN64: store i8* %ap, i8** %[[AP:.*]]
+  int b = __builtin_va_arg(ap, int);
+  // FREEBSD: %[[AP_CUR:.*]] = load i8*, i8** %[[AP]]
+  // FREEBSD-NEXT: %[[AP_NEXT:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR]], i64 8
+  // FREEBSD-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]]
+  // FREEBSD-NEXT: bitcast i8* %[[AP_CUR]] to i32*
+  // WIN64: %[[AP_CUR:.*]] = load i8*, i8** %[[AP]]
+  // WIN64-NEXT: %[[AP_NEXT:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR]], i64 8
+  // WIN64-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]]
+  // WIN64-NEXT: bitcast i8* %[[AP_CUR]] to i32*
+  double _Complex c = __builtin_va_arg(ap, double _Complex);
+  // FREEBSD: %[[AP_CUR2:.*]] = load i8*, i8** %[[AP]]
+  // FREEBSD-NEXT: %[[AP_NEXT2:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR2]], i64 16
+  // FREEBSD-NEXT: store i8* %[[AP_NEXT2]], i8** %[[AP]]
+  // FREEBSD-NEXT: bitcast i8* %[[AP_CUR2]] to { double, double }*
+  // WIN64: %[[AP_CUR2:.*]] = load i8*, i8** %[[AP]]
+  // WIN64-NEXT: %[[AP_NEXT2:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR2]], i64 16
+  // WIN64-NEXT: store i8* %[[AP_NEXT2]], i8** %[[AP]]
+  // WIN64-NEXT: bitcast i8* %[[AP_CUR2]] to { double, double }*
+  struct foo d = __builtin_va_arg(ap, struct foo);
+  // FREEBSD: %[[AP_CUR3:.*]] = load i8*, i8** %[[AP]]
+  // FREEBSD-NEXT: %[[AP_NEXT3:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR3]], i64 16
+  // FREEBSD-NEXT: store i8* %[[AP_NEXT3]], i8** %[[AP]]
+  // FREEBSD-NEXT: bitcast i8* %[[AP_CUR3]] to %[[STRUCT_FOO]]*
+  // WIN64: %[[AP_CUR3:.*]] = load i8*, i8** %[[AP]]
+  // WIN64-NEXT: %[[AP_NEXT3:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR3]], i64 16
+  // WIN64-NEXT: store i8* %[[AP_NEXT3]], i8** %[[AP]]
+  // WIN64-NEXT: bitcast i8* %[[AP_CUR3]] to %[[STRUCT_FOO]]*
+  __builtin_ms_va_list ap2;
+  __builtin_ms_va_copy(ap2, ap);
+  // FREEBSD: %[[AP_VAL:.*]] = load i8*, i8** %[[AP]]
+  // FREEBSD-NEXT: store i8* %[[AP_VAL]], i8** %[[AP2:.*]]
+  // WIN64: %[[AP_VAL:.*]] = load i8*, i8** %[[AP]]
+  // WIN64-NEXT: store i8* %[[AP_VAL]], i8** %[[AP2:.*]]
+}
diff --git a/clang/test/PCH/Inputs/va_arg.h b/clang/test/PCH/Inputs/va_arg.h
index 1244e9f..132759e 100644
--- a/clang/test/PCH/Inputs/va_arg.h
+++ b/clang/test/PCH/Inputs/va_arg.h
@@ -1,2 +1,5 @@
 #include <stdarg.h>
 
+typedef __builtin_ms_va_list __ms_va_list;
+#define __ms_va_start(ap, a) __builtin_ms_va_start(ap, a)
+#define __ms_va_end(ap) __builtin_ms_va_end(ap)
diff --git a/clang/test/PCH/va_arg.c b/clang/test/PCH/va_arg.c
index dba9eea..2bbf3c5 100644
--- a/clang/test/PCH/va_arg.c
+++ b/clang/test/PCH/va_arg.c
@@ -11,3 +11,9 @@
 char *g(char **argv) {
   f(g0, argv, 1, 2, 3);
 }
+
+char *i0(char **argv, int argc) { return argv[argc]; }
+
+char *i(char **argv) {
+  h(i0, argv, 1, 2, 3);
+}
diff --git a/clang/test/PCH/va_arg.cpp b/clang/test/PCH/va_arg.cpp
index 0b3c3b1..627ce23 100644
--- a/clang/test/PCH/va_arg.cpp
+++ b/clang/test/PCH/va_arg.cpp
@@ -10,8 +10,13 @@
 
 extern "C" {
 int vsnprintf(char * , size_t, const char * , va_list) ;
+int __attribute__((ms_abi)) wvsprintfA(char *, const char *, __ms_va_list);
 }
 
 void f(char *buffer, unsigned count, const char* format, va_list argptr) {
   vsnprintf(buffer, count, format, argptr);
 }
+
+void g(char *buffer, const char *format, __ms_va_list argptr) {
+  wvsprintfA(buffer, format, argptr);
+}
diff --git a/clang/test/PCH/va_arg.h b/clang/test/PCH/va_arg.h
index 4a8e510..255c658 100644
--- a/clang/test/PCH/va_arg.h
+++ b/clang/test/PCH/va_arg.h
@@ -6,3 +6,10 @@
     va_list v;
     s = g (p, __builtin_va_arg(v, int));
 }
+
+typedef __builtin_ms_va_list __ms_va_list;
+char *__attribute__((ms_abi)) h(char *(*i)(char **, int), char **p, ...) {
+  char *s;
+  __ms_va_list v;
+  s = i(p, __builtin_va_arg(v, int));
+}
diff --git a/clang/test/Sema/varargs-win64.c b/clang/test/Sema/varargs-win64.c
new file mode 100644
index 0000000..06d1c7f
--- /dev/null
+++ b/clang/test/Sema/varargs-win64.c
@@ -0,0 +1,6 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -triple x86_64-pc-win32
+
+void __attribute__((sysv_abi)) foo(int a, ...) {
+  __builtin_va_list ap;
+  __builtin_va_start(ap, a); // expected-error {{'va_start' used in System V ABI function}}
+}
diff --git a/clang/test/Sema/varargs-x86-32.c b/clang/test/Sema/varargs-x86-32.c
new file mode 100644
index 0000000..6f57022
--- /dev/null
+++ b/clang/test/Sema/varargs-x86-32.c
@@ -0,0 +1,5 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -triple i386-apple-darwin9
+
+void foo(int a, ...) {
+  __builtin_ms_va_start((void *)0, a); // expected-error {{this builtin is only available on x86-64 targets}}
+}
diff --git a/clang/test/Sema/varargs-x86-64.c b/clang/test/Sema/varargs-x86-64.c
index 2fe9b10..d50dd6a 100644
--- a/clang/test/Sema/varargs-x86-64.c
+++ b/clang/test/Sema/varargs-x86-64.c
@@ -6,3 +6,75 @@
   (void)__builtin_va_arg(args2, int); // expected-error {{first argument to 'va_arg' is of type 'const __builtin_va_list' and not 'va_list'}}
 }
 
+void f2(int a, ...) {
+  __builtin_ms_va_list ap;
+  __builtin_ms_va_start(ap, a); // expected-error {{'__builtin_ms_va_start' used in System V ABI function}}
+}
+
+void __attribute__((ms_abi)) g1(int a) {
+  __builtin_ms_va_list ap;
+
+  __builtin_ms_va_start(ap, a, a); // expected-error {{too many arguments to function}}
+  __builtin_ms_va_start(ap, a); // expected-error {{'va_start' used in function with fixed args}}
+}
+
+void __attribute__((ms_abi)) g2(int a, int b, ...) {
+  __builtin_ms_va_list ap;
+
+  __builtin_ms_va_start(ap, 10); // expected-warning {{second parameter of 'va_start' not last named argument}}
+  __builtin_ms_va_start(ap, a); // expected-warning {{second parameter of 'va_start' not last named argument}}
+  __builtin_ms_va_start(ap, b);
+}
+
+void __attribute__((ms_abi)) g3(float a, ...) {
+  __builtin_ms_va_list ap;
+
+  __builtin_ms_va_start(ap, a);
+  __builtin_ms_va_start(ap, (a));
+}
+
+void __attribute__((ms_abi)) g5() {
+  __builtin_ms_va_list ap;
+  __builtin_ms_va_start(ap, ap); // expected-error {{'va_start' used in function with fixed args}}
+}
+
+void __attribute__((ms_abi)) g6(int a, ...) {
+  __builtin_ms_va_list ap;
+  __builtin_ms_va_start(ap); // expected-error {{too few arguments to function}}
+}
+
+void __attribute__((ms_abi))
+bar(__builtin_ms_va_list authors, ...) {
+  __builtin_ms_va_start(authors, authors);
+  (void)__builtin_va_arg(authors, int);
+  __builtin_ms_va_end(authors);
+}
+
+void __attribute__((ms_abi)) g7(int a, ...) {
+  __builtin_ms_va_list ap;
+  __builtin_ms_va_start(ap, a);
+  // FIXME: This error message is sub-par.
+  __builtin_va_arg(ap, int) = 1; // expected-error {{expression is not assignable}}
+  int *x = &__builtin_va_arg(ap, int); // expected-error {{cannot take the address of an rvalue}}
+  __builtin_ms_va_end(ap);
+}
+
+void __attribute__((ms_abi)) g8(int a, ...) {
+  __builtin_ms_va_list ap;
+  __builtin_ms_va_start(ap, a);
+  (void)__builtin_va_arg(ap, void); // expected-error {{second argument to 'va_arg' is of incomplete type 'void'}}
+  __builtin_ms_va_end(ap);
+}
+
+enum E { x = -1, y = 2, z = 10000 };
+void __attribute__((ms_abi)) g9(__builtin_ms_va_list args) {
+  (void)__builtin_va_arg(args, float); // expected-warning {{second argument to 'va_arg' is of promotable type 'float'}}
+  (void)__builtin_va_arg(args, enum E); // no-warning
+  (void)__builtin_va_arg(args, short); // expected-warning {{second argument to 'va_arg' is of promotable type 'short'}}
+  (void)__builtin_va_arg(args, char); // expected-warning {{second argument to 'va_arg' is of promotable type 'char'}}
+}
+
+void __attribute__((ms_abi)) g10(int a, ...) {
+  __builtin_va_list ap;
+  __builtin_va_start(ap, a); // expected-error {{'va_start' used in Win64 ABI function}}
+}
diff --git a/clang/test/SemaTemplate/instantiate-expr-3.cpp b/clang/test/SemaTemplate/instantiate-expr-3.cpp
index ca88b00..90c322c 100644
--- a/clang/test/SemaTemplate/instantiate-expr-3.cpp
+++ b/clang/test/SemaTemplate/instantiate-expr-3.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify %s
 
 // ---------------------------------------------------------------------
 // Imaginary literals
@@ -108,12 +108,41 @@
 struct VaArg1 {
   void f(int n, ...) {
     VaList va;
-    __builtin_va_start(va, n); // expected-error{{int}}
+    __builtin_va_start(va, n); // expected-error{{int}} expected-error{{char *}}
     for (int i = 0; i != n; ++i)
       (void)__builtin_va_arg(va, ArgType); // expected-error{{int}}
-    __builtin_va_end(va); // expected-error{{int}}
+    __builtin_va_end(va); // expected-error{{int}} expected-error{{char *}}
   }
 };
 
 template struct VaArg1<__builtin_va_list, int>;
+template struct VaArg1<__builtin_ms_va_list, int>; // expected-note{{instantiation}}
 template struct VaArg1<int, int>; // expected-note{{instantiation}}
+
+template<typename ArgType>
+struct VaArg2 {
+  void __attribute__((ms_abi)) f(int n, ...) {
+    __builtin_ms_va_list va;
+    __builtin_ms_va_start(va, n);
+    for (int i = 0; i != n; ++i)
+      (void)__builtin_va_arg(va, ArgType);
+    __builtin_ms_va_end(va);
+  }
+};
+
+template struct VaArg2<int>;
+
+template<typename VaList, typename ArgType>
+struct VaArg3 {
+  void __attribute__((ms_abi)) f(int n, ...) {
+    VaList va;
+    __builtin_ms_va_start(va, n); // expected-error{{int}} expected-error{{__va_list_tag}}
+    for (int i = 0; i != n; ++i)
+      (void)__builtin_va_arg(va, ArgType); // expected-error{{int}}
+    __builtin_ms_va_end(va); // expected-error{{int}} expected-error{{__va_list_tag}}
+  }
+};
+
+template struct VaArg3<__builtin_ms_va_list, int>;
+template struct VaArg3<__builtin_va_list, int>; // expected-note{{instantiation}}
+template struct VaArg3<int, int>; // expected-note{{instantiation}}