Clang Project

clang_source_code/test/CodeGen/ms_abi.c
1// RUN: %clang_cc1 -triple x86_64-unknown-freebsd10.0 -emit-llvm < %s | FileCheck -check-prefix=FREEBSD %s
2// RUN: %clang_cc1 -triple x86_64-pc-win32 -emit-llvm < %s | FileCheck -check-prefix=WIN64 %s
3
4struct foo {
5  int x;
6  float y;
7  char z;
8};
9// FREEBSD: %[[STRUCT_FOO:.*]] = type { i32, float, i8 }
10// WIN64: %[[STRUCT_FOO:.*]] = type { i32, float, i8 }
11
12void __attribute__((ms_abi)) f1(void);
13void __attribute__((sysv_abi)) f2(void);
14void f3(void) {
15  // FREEBSD-LABEL: define void @f3()
16  // WIN64-LABEL: define dso_local void @f3()
17  f1();
18  // FREEBSD: call win64cc void @f1()
19  // WIN64: call void @f1()
20  f2();
21  // FREEBSD: call void @f2()
22  // WIN64: call x86_64_sysvcc void @f2()
23}
24// FREEBSD: declare win64cc void @f1()
25// FREEBSD: declare void @f2()
26// WIN64: declare dso_local void @f1()
27// WIN64: declare dso_local x86_64_sysvcc void @f2()
28
29// Win64 ABI varargs
30void __attribute__((ms_abi)) f4(int a, ...) {
31  // FREEBSD-LABEL: define win64cc void @f4
32  // WIN64-LABEL: define dso_local void @f4
33  __builtin_ms_va_list ap;
34  __builtin_ms_va_start(ap, a);
35  // FREEBSD: %[[AP:.*]] = alloca i8*
36  // FREEBSD: call void @llvm.va_start
37  // WIN64: %[[AP:.*]] = alloca i8*
38  // WIN64: call void @llvm.va_start
39  int b = __builtin_va_arg(ap, int);
40  // FREEBSD: %[[AP_CUR:.*]] = load i8*, i8** %[[AP]]
41  // FREEBSD-NEXT: %[[AP_NEXT:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR]], i64 8
42  // FREEBSD-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]]
43  // FREEBSD-NEXT: bitcast i8* %[[AP_CUR]] to i32*
44  // WIN64: %[[AP_CUR:.*]] = load i8*, i8** %[[AP]]
45  // WIN64-NEXT: %[[AP_NEXT:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR]], i64 8
46  // WIN64-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]]
47  // WIN64-NEXT: bitcast i8* %[[AP_CUR]] to i32*
48  // FIXME: These are different now. We probably need __builtin_ms_va_arg.
49  double _Complex c = __builtin_va_arg(ap, double _Complex);
50  // FREEBSD: %[[AP_CUR2:.*]] = load i8*, i8** %[[AP]]
51  // FREEBSD-NEXT: %[[AP_NEXT2:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR2]], i64 16
52  // FREEBSD-NEXT: store i8* %[[AP_NEXT2]], i8** %[[AP]]
53  // FREEBSD-NEXT: bitcast i8* %[[AP_CUR2]] to { double, double }*
54  // WIN64: %[[AP_CUR2:.*]] = load i8*, i8** %[[AP]]
55  // WIN64-NEXT: %[[AP_NEXT2:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR2]], i64 8
56  // WIN64-NEXT: store i8* %[[AP_NEXT2]], i8** %[[AP]]
57  // WIN64-NEXT: %[[CUR2:.*]] = bitcast i8* %[[AP_CUR2]] to { double, double }**
58  // WIN64-NEXT: load { double, double }*, { double, double }** %[[CUR2]]
59  struct foo d = __builtin_va_arg(ap, struct foo);
60  // FREEBSD: %[[AP_CUR3:.*]] = load i8*, i8** %[[AP]]
61  // FREEBSD-NEXT: %[[AP_NEXT3:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR3]], i64 16
62  // FREEBSD-NEXT: store i8* %[[AP_NEXT3]], i8** %[[AP]]
63  // FREEBSD-NEXT: bitcast i8* %[[AP_CUR3]] to %[[STRUCT_FOO]]*
64  // WIN64: %[[AP_CUR3:.*]] = load i8*, i8** %[[AP]]
65  // WIN64-NEXT: %[[AP_NEXT3:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR3]], i64 8
66  // WIN64-NEXT: store i8* %[[AP_NEXT3]], i8** %[[AP]]
67  // WIN64-NEXT: %[[CUR3:.*]] = bitcast i8* %[[AP_CUR3]] to %[[STRUCT_FOO]]*
68  // WIN64-NEXT: load %[[STRUCT_FOO]]*, %[[STRUCT_FOO]]** %[[CUR3]]
69  __builtin_ms_va_list ap2;
70  __builtin_ms_va_copy(ap2, ap);
71  // FREEBSD: %[[AP_VAL:.*]] = load i8*, i8** %[[AP]]
72  // FREEBSD-NEXT: store i8* %[[AP_VAL]], i8** %[[AP2:.*]]
73  // WIN64: %[[AP_VAL:.*]] = load i8*, i8** %[[AP]]
74  // WIN64-NEXT: store i8* %[[AP_VAL]], i8** %[[AP2:.*]]
75  __builtin_ms_va_end(ap);
76  // FREEBSD: call void @llvm.va_end
77  // WIN64: call void @llvm.va_end
78}
79
80// Let's verify that normal va_lists work right on Win64, too.
81void f5(int a, ...) {
82  // WIN64-LABEL: define dso_local void @f5
83  __builtin_va_list ap;
84  __builtin_va_start(ap, a);
85  // WIN64: %[[AP:.*]] = alloca i8*
86  // WIN64: call void @llvm.va_start
87  int b = __builtin_va_arg(ap, int);
88  // WIN64: %[[AP_CUR:.*]] = load i8*, i8** %[[AP]]
89  // WIN64-NEXT: %[[AP_NEXT:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR]], i64 8
90  // WIN64-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]]
91  // WIN64-NEXT: bitcast i8* %[[AP_CUR]] to i32*
92  double _Complex c = __builtin_va_arg(ap, double _Complex);
93  // WIN64: %[[AP_CUR2:.*]] = load i8*, i8** %[[AP]]
94  // WIN64-NEXT: %[[AP_NEXT2:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR2]], i64 8
95  // WIN64-NEXT: store i8* %[[AP_NEXT2]], i8** %[[AP]]
96  // WIN64-NEXT: bitcast i8* %[[AP_CUR2]] to { double, double }*
97  struct foo d = __builtin_va_arg(ap, struct foo);
98  // WIN64: %[[AP_CUR3:.*]] = load i8*, i8** %[[AP]]
99  // WIN64-NEXT: %[[AP_NEXT3:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR3]], i64 8
100  // WIN64-NEXT: store i8* %[[AP_NEXT3]], i8** %[[AP]]
101  // WIN64-NEXT: bitcast i8* %[[AP_CUR3]] to %[[STRUCT_FOO]]*
102  __builtin_va_list ap2;
103  __builtin_va_copy(ap2, ap);
104  // WIN64: call void @llvm.va_copy
105  __builtin_va_end(ap);
106  // WIN64: call void @llvm.va_end
107}
108
109// Verify that using a Win64 va_list from a System V function works.
110void __attribute__((sysv_abi)) f6(__builtin_ms_va_list ap) {
111  // FREEBSD-LABEL: define void @f6
112  // FREEBSD: store i8* %ap, i8** %[[AP:.*]]
113  // WIN64-LABEL: define dso_local x86_64_sysvcc void @f6
114  // WIN64: store i8* %ap, i8** %[[AP:.*]]
115  int b = __builtin_va_arg(ap, int);
116  // FREEBSD: %[[AP_CUR:.*]] = load i8*, i8** %[[AP]]
117  // FREEBSD-NEXT: %[[AP_NEXT:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR]], i64 8
118  // FREEBSD-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]]
119  // FREEBSD-NEXT: bitcast i8* %[[AP_CUR]] to i32*
120  // WIN64: %[[AP_CUR:.*]] = load i8*, i8** %[[AP]]
121  // WIN64-NEXT: %[[AP_NEXT:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR]], i64 8
122  // WIN64-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]]
123  // WIN64-NEXT: bitcast i8* %[[AP_CUR]] to i32*
124  double _Complex c = __builtin_va_arg(ap, double _Complex);
125  // FREEBSD: %[[AP_CUR2:.*]] = load i8*, i8** %[[AP]]
126  // FREEBSD-NEXT: %[[AP_NEXT2:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR2]], i64 16
127  // FREEBSD-NEXT: store i8* %[[AP_NEXT2]], i8** %[[AP]]
128  // FREEBSD-NEXT: bitcast i8* %[[AP_CUR2]] to { double, double }*
129  // WIN64: %[[AP_CUR2:.*]] = load i8*, i8** %[[AP]]
130  // WIN64-NEXT: %[[AP_NEXT2:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR2]], i64 8
131  // WIN64-NEXT: store i8* %[[AP_NEXT2]], i8** %[[AP]]
132  // WIN64-NEXT: bitcast i8* %[[AP_CUR2]] to { double, double }*
133  struct foo d = __builtin_va_arg(ap, struct foo);
134  // FREEBSD: %[[AP_CUR3:.*]] = load i8*, i8** %[[AP]]
135  // FREEBSD-NEXT: %[[AP_NEXT3:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR3]], i64 16
136  // FREEBSD-NEXT: store i8* %[[AP_NEXT3]], i8** %[[AP]]
137  // FREEBSD-NEXT: bitcast i8* %[[AP_CUR3]] to %[[STRUCT_FOO]]*
138  // WIN64: %[[AP_CUR3:.*]] = load i8*, i8** %[[AP]]
139  // WIN64-NEXT: %[[AP_NEXT3:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR3]], i64 8
140  // WIN64-NEXT: store i8* %[[AP_NEXT3]], i8** %[[AP]]
141  // WIN64-NEXT: bitcast i8* %[[AP_CUR3]] to %[[STRUCT_FOO]]*
142  __builtin_ms_va_list ap2;
143  __builtin_ms_va_copy(ap2, ap);
144  // FREEBSD: %[[AP_VAL:.*]] = load i8*, i8** %[[AP]]
145  // FREEBSD-NEXT: store i8* %[[AP_VAL]], i8** %[[AP2:.*]]
146  // WIN64: %[[AP_VAL:.*]] = load i8*, i8** %[[AP]]
147  // WIN64-NEXT: store i8* %[[AP_VAL]], i8** %[[AP2:.*]]
148}
149
150// This test checks if structs are passed according to Win64 calling convention
151// when it's enforced by __attribute((ms_abi)).
152struct i128 {
153  unsigned long long a;
154  unsigned long long b;
155};
156
157__attribute__((ms_abi)) struct i128 f7(struct i128 a) {
158  // WIN64: define dso_local void @f7(%struct.i128* noalias sret %agg.result, %struct.i128* %a)
159  // FREEBSD: define win64cc void @f7(%struct.i128* noalias sret %agg.result, %struct.i128* %a)
160  return a;
161}
162