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}}