1 // RUN: %clang_cc1 -triple sparcv9-unknown-unknown -emit-llvm %s -o - | FileCheck %s
2 #include <stdarg.h>
3
4 // CHECK-LABEL: define{{.*}} void @f_void()
f_void(void)5 void f_void(void) {}
6
7 // Arguments and return values smaller than the word size are extended.
8
9 // CHECK-LABEL: define{{.*}} signext i32 @f_int_1(i32 noundef signext %x)
f_int_1(int x)10 int f_int_1(int x) { return x; }
11
12 // CHECK-LABEL: define{{.*}} zeroext i32 @f_int_2(i32 noundef zeroext %x)
f_int_2(unsigned x)13 unsigned f_int_2(unsigned x) { return x; }
14
15 // CHECK-LABEL: define{{.*}} i64 @f_int_3(i64 noundef %x)
f_int_3(long long x)16 long long f_int_3(long long x) { return x; }
17
18 // CHECK-LABEL: define{{.*}} signext i8 @f_int_4(i8 noundef signext %x)
f_int_4(char x)19 char f_int_4(char x) { return x; }
20
21 // CHECK-LABEL: define{{.*}} fp128 @f_ld(fp128 noundef %x)
f_ld(long double x)22 long double f_ld(long double x) { return x; }
23
24 // Zero-sized structs reserves an argument register slot if passed directly.
25 struct empty {};
26 struct emptyarr { struct empty a[10]; };
27
28 // CHECK-LABEL: define{{.*}} i64 @f_empty(i64 %x.coerce)
f_empty(struct empty x)29 struct empty f_empty(struct empty x) { return x; }
30
31 // CHECK-LABEL: define{{.*}} i64 @f_emptyarr(i64 %x.coerce)
f_emptyarr(struct emptyarr x)32 struct empty f_emptyarr(struct emptyarr x) { return x.a[0]; }
33
34 // CHECK-LABEL: define{{.*}} i64 @f_emptyvar(i32 noundef zeroext %count, ...)
f_emptyvar(unsigned count,...)35 long f_emptyvar(unsigned count, ...) {
36 long ret;
37 va_list args;
38 va_start(args, count);
39
40 // CHECK: %[[CUR:[^ ]+]] = load ptr, ptr %args
41 // CHECK-DAG: %[[NXT:[^ ]+]] = getelementptr inbounds i8, ptr %[[CUR]], i64 8
42 // CHECK-DAG: store ptr %[[NXT]], ptr %args
43 va_arg(args, struct empty);
44
45 // CHECK: %[[CUR:[^ ]+]] = load ptr, ptr %args
46 // CHECK-DAG: %[[NXT:[^ ]+]] = getelementptr inbounds i8, ptr %[[CUR]], i64 8
47 // CHECK-DAG: store ptr %[[NXT]], ptr %args
48 // CHECK-DAG: load i64, ptr %[[CUR]]
49 ret = va_arg(args, long);
50 va_end(args);
51 return ret;
52 }
53
54 // If the zero-sized struct is contained in a non-zero-sized struct,
55 // though, it doesn't reserve any registers.
56 struct emptymixed { struct empty a; long b; };
57 struct emptyflex { unsigned count; struct empty data[10]; };
58
59 // CHECK-LABEL: define{{.*}} i64 @f_emptymixed(i64 %x.coerce)
f_emptymixed(struct emptymixed x)60 long f_emptymixed(struct emptymixed x) { return x.b; }
61
62 // CHECK-LABEL: define{{.*}} i64 @f_emptyflex(i64 %x.coerce, i64 noundef %y)
f_emptyflex(struct emptyflex x,long y)63 long f_emptyflex(struct emptyflex x, long y) { return y; }
64
65 // Small structs are passed in registers.
66 struct small {
67 int *a, *b;
68 };
69
70 // CHECK-LABEL: define{{.*}} %struct.small @f_small(ptr %x.coerce0, ptr %x.coerce1)
f_small(struct small x)71 struct small f_small(struct small x) {
72 x.a += *x.b;
73 x.b = 0;
74 return x;
75 }
76
77 // Medium-sized structs are passed indirectly, but can be returned in registers.
78 struct medium {
79 int *a, *b;
80 int *c, *d;
81 };
82
83 // CHECK-LABEL: define{{.*}} %struct.medium @f_medium(ptr noundef %x)
f_medium(struct medium x)84 struct medium f_medium(struct medium x) {
85 x.a += *x.b;
86 x.b = 0;
87 return x;
88 }
89
90 // Large structs are also returned indirectly.
91 struct large {
92 int *a, *b;
93 int *c, *d;
94 int x;
95 };
96
97 // CHECK-LABEL: define{{.*}} void @f_large(ptr dead_on_unwind noalias writable sret(%struct.large) align 8 %agg.result, ptr noundef %x)
f_large(struct large x)98 struct large f_large(struct large x) {
99 x.a += *x.b;
100 x.b = 0;
101 return x;
102 }
103
104 // A 64-bit struct fits in a register.
105 struct reg {
106 int a, b;
107 };
108
109 // CHECK-LABEL: define{{.*}} i64 @f_reg(i64 %x.coerce)
f_reg(struct reg x)110 struct reg f_reg(struct reg x) {
111 x.a += x.b;
112 return x;
113 }
114
115 // Structs with mixed int and float parts require the inreg attribute.
116 struct mixed {
117 int a;
118 float b;
119 };
120
121 // CHECK-LABEL: define{{.*}} inreg %struct.mixed @f_mixed(i32 inreg %x.coerce0, float inreg %x.coerce1)
f_mixed(struct mixed x)122 struct mixed f_mixed(struct mixed x) {
123 x.a += 1;
124 return x;
125 }
126
127 // Struct with padding.
128 struct mixed2 {
129 int a;
130 double b;
131 };
132
133 // CHECK: define{{.*}} { i64, double } @f_mixed2(i64 %x.coerce0, double %x.coerce1)
134 // CHECK: store i64 %x.coerce0
135 // CHECK: store double %x.coerce1
f_mixed2(struct mixed2 x)136 struct mixed2 f_mixed2(struct mixed2 x) {
137 x.a += 1;
138 return x;
139 }
140
141 // Struct with single element and padding in passed in the high bits of a
142 // register.
143 struct tiny {
144 char a;
145 };
146
147 // CHECK-LABEL: define{{.*}} i64 @f_tiny(i64 %x.coerce)
148 // CHECK: %[[HB:[^ ]+]] = lshr i64 %x.coerce, 56
149 // CHECK: = trunc i64 %[[HB]] to i8
f_tiny(struct tiny x)150 struct tiny f_tiny(struct tiny x) {
151 x.a += 1;
152 return x;
153 }
154
155 // CHECK-LABEL: define{{.*}} void @call_tiny()
156 // CHECK: %[[XV:[^ ]+]] = zext i8 %{{[^ ]+}} to i64
157 // CHECK: %[[HB:[^ ]+]] = shl i64 %[[XV]], 56
158 // CHECK: = call i64 @f_tiny(i64 %[[HB]])
call_tiny(void)159 void call_tiny(void) {
160 struct tiny x = { 1 };
161 f_tiny(x);
162 }
163
164 // CHECK-LABEL: define{{.*}} signext i32 @f_variable(ptr noundef %f, ...)
165 // CHECK: %ap = alloca ptr
166 // CHECK: call void @llvm.va_start
167 //
f_variable(char * f,...)168 int f_variable(char *f, ...) {
169 int s = 0;
170 char c;
171 va_list ap;
172 va_start(ap, f);
173 while ((c = *f++)) switch (c) {
174
175 // CHECK: %[[CUR:[^ ]+]] = load ptr, ptr %ap
176 // CHECK-DAG: %[[NXT:[^ ]+]] = getelementptr inbounds i8, ptr %[[CUR]], i64 8
177 // CHECK-DAG: store ptr %[[NXT]], ptr %ap
178 // CHECK-DAG: %[[EXT:[^ ]+]] = getelementptr inbounds i8, ptr %[[CUR]], i64 4
179 // CHECK-DAG: load i32, ptr %[[EXT]]
180 // CHECK: br
181 case 'i':
182 s += va_arg(ap, int);
183 break;
184
185 // CHECK: %[[CUR:[^ ]+]] = load ptr, ptr %ap
186 // CHECK-DAG: %[[NXT:[^ ]+]] = getelementptr inbounds i8, ptr %[[CUR]], i64 8
187 // CHECK-DAG: store ptr %[[NXT]], ptr %ap
188 // CHECK-DAG: load i64, ptr %[[CUR]]
189 // CHECK: br
190 case 'l':
191 s += va_arg(ap, long);
192 break;
193
194 // CHECK: %[[CUR:[^ ]+]] = load ptr, ptr %ap
195 // CHECK-DAG: %[[NXT:[^ ]+]] = getelementptr inbounds i8, ptr %[[CUR]], i64 8
196 // CHECK-DAG: store ptr %[[NXT]], ptr %ap
197 // CHECK: br
198 case 't':
199 s += va_arg(ap, struct tiny).a;
200 break;
201
202 // CHECK: %[[CUR:[^ ]+]] = load ptr, ptr %ap
203 // CHECK-DAG: %[[NXT:[^ ]+]] = getelementptr inbounds i8, ptr %[[CUR]], i64 16
204 // CHECK-DAG: store ptr %[[NXT]], ptr %ap
205 // CHECK: br
206 case 's':
207 s += *va_arg(ap, struct small).a;
208 break;
209
210 // CHECK: %[[CUR:[^ ]+]] = load ptr, ptr %ap
211 // CHECK-DAG: %[[NXT:[^ ]+]] = getelementptr inbounds i8, ptr %[[CUR]], i64 8
212 // CHECK-DAG: store ptr %[[NXT]], ptr %ap
213 // CHECK-DAG: %[[ADR:[^ ]+]] = load ptr, ptr %[[CUR]]
214 // CHECK: br
215 case 'm':
216 s += *va_arg(ap, struct medium).a;
217 break;
218 }
219 return s;
220 }
221