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() |
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 signext %x) |
10 | int f_int_1(int x) { return x; } |
11 | |
12 | // CHECK-LABEL: define zeroext i32 @f_int_2(i32 zeroext %x) |
13 | unsigned f_int_2(unsigned x) { return x; } |
14 | |
15 | // CHECK-LABEL: define i64 @f_int_3(i64 %x) |
16 | long long f_int_3(long long x) { return x; } |
17 | |
18 | // CHECK-LABEL: define signext i8 @f_int_4(i8 signext %x) |
19 | char f_int_4(char x) { return x; } |
20 | |
21 | // CHECK-LABEL: define fp128 @f_ld(fp128 %x) |
22 | long double f_ld(long double x) { return x; } |
23 | |
24 | // Small structs are passed in registers. |
25 | struct small { |
26 | int *a, *b; |
27 | }; |
28 | |
29 | // CHECK-LABEL: define %struct.small @f_small(i32* %x.coerce0, i32* %x.coerce1) |
30 | struct small f_small(struct small x) { |
31 | x.a += *x.b; |
32 | x.b = 0; |
33 | return x; |
34 | } |
35 | |
36 | // Medium-sized structs are passed indirectly, but can be returned in registers. |
37 | struct medium { |
38 | int *a, *b; |
39 | int *c, *d; |
40 | }; |
41 | |
42 | // CHECK-LABEL: define %struct.medium @f_medium(%struct.medium* %x) |
43 | struct medium f_medium(struct medium x) { |
44 | x.a += *x.b; |
45 | x.b = 0; |
46 | return x; |
47 | } |
48 | |
49 | // Large structs are also returned indirectly. |
50 | struct large { |
51 | int *a, *b; |
52 | int *c, *d; |
53 | int x; |
54 | }; |
55 | |
56 | // CHECK-LABEL: define void @f_large(%struct.large* noalias sret %agg.result, %struct.large* %x) |
57 | struct large f_large(struct large x) { |
58 | x.a += *x.b; |
59 | x.b = 0; |
60 | return x; |
61 | } |
62 | |
63 | // A 64-bit struct fits in a register. |
64 | struct reg { |
65 | int a, b; |
66 | }; |
67 | |
68 | // CHECK-LABEL: define i64 @f_reg(i64 %x.coerce) |
69 | struct reg f_reg(struct reg x) { |
70 | x.a += x.b; |
71 | return x; |
72 | } |
73 | |
74 | // Structs with mixed int and float parts require the inreg attribute. |
75 | struct mixed { |
76 | int a; |
77 | float b; |
78 | }; |
79 | |
80 | // CHECK-LABEL: define inreg %struct.mixed @f_mixed(i32 inreg %x.coerce0, float inreg %x.coerce1) |
81 | struct mixed f_mixed(struct mixed x) { |
82 | x.a += 1; |
83 | return x; |
84 | } |
85 | |
86 | // Struct with padding. |
87 | struct mixed2 { |
88 | int a; |
89 | double b; |
90 | }; |
91 | |
92 | // CHECK: define { i64, double } @f_mixed2(i64 %x.coerce0, double %x.coerce1) |
93 | // CHECK: store i64 %x.coerce0 |
94 | // CHECK: store double %x.coerce1 |
95 | struct mixed2 f_mixed2(struct mixed2 x) { |
96 | x.a += 1; |
97 | return x; |
98 | } |
99 | |
100 | // Struct with single element and padding in passed in the high bits of a |
101 | // register. |
102 | struct tiny { |
103 | char a; |
104 | }; |
105 | |
106 | // CHECK-LABEL: define i64 @f_tiny(i64 %x.coerce) |
107 | // CHECK: %[[HB:[^ ]+]] = lshr i64 %x.coerce, 56 |
108 | // CHECK: = trunc i64 %[[HB]] to i8 |
109 | struct tiny f_tiny(struct tiny x) { |
110 | x.a += 1; |
111 | return x; |
112 | } |
113 | |
114 | // CHECK-LABEL: define void @call_tiny() |
115 | // CHECK: %[[XV:[^ ]+]] = zext i8 %{{[^ ]+}} to i64 |
116 | // CHECK: %[[HB:[^ ]+]] = shl i64 %[[XV]], 56 |
117 | // CHECK: = call i64 @f_tiny(i64 %[[HB]]) |
118 | void call_tiny() { |
119 | struct tiny x = { 1 }; |
120 | f_tiny(x); |
121 | } |
122 | |
123 | // CHECK-LABEL: define signext i32 @f_variable(i8* %f, ...) |
124 | // CHECK: %ap = alloca i8* |
125 | // CHECK: call void @llvm.va_start |
126 | // |
127 | int f_variable(char *f, ...) { |
128 | int s = 0; |
129 | char c; |
130 | va_list ap; |
131 | va_start(ap, f); |
132 | while ((c = *f++)) switch (c) { |
133 | |
134 | // CHECK: %[[CUR:[^ ]+]] = load i8*, i8** %ap |
135 | // CHECK-DAG: %[[NXT:[^ ]+]] = getelementptr inbounds i8, i8* %[[CUR]], i64 8 |
136 | // CHECK-DAG: store i8* %[[NXT]], i8** %ap |
137 | // CHECK-DAG: %[[EXT:[^ ]+]] = getelementptr inbounds i8, i8* %[[CUR]], i64 4 |
138 | // CHECK-DAG: %[[ADR:[^ ]+]] = bitcast i8* %[[EXT]] to i32* |
139 | // CHECK-DAG: load i32, i32* %[[ADR]] |
140 | // CHECK: br |
141 | case 'i': |
142 | s += va_arg(ap, int); |
143 | break; |
144 | |
145 | // CHECK: %[[CUR:[^ ]+]] = load i8*, i8** %ap |
146 | // CHECK-DAG: %[[NXT:[^ ]+]] = getelementptr inbounds i8, i8* %[[CUR]], i64 8 |
147 | // CHECK-DAG: store i8* %[[NXT]], i8** %ap |
148 | // CHECK-DAG: %[[ADR:[^ ]+]] = bitcast i8* %[[CUR]] to i64* |
149 | // CHECK-DAG: load i64, i64* %[[ADR]] |
150 | // CHECK: br |
151 | case 'l': |
152 | s += va_arg(ap, long); |
153 | break; |
154 | |
155 | // CHECK: %[[CUR:[^ ]+]] = load i8*, i8** %ap |
156 | // CHECK-DAG: %[[NXT:[^ ]+]] = getelementptr inbounds i8, i8* %[[CUR]], i64 8 |
157 | // CHECK-DAG: store i8* %[[NXT]], i8** %ap |
158 | // CHECK-DAG: %[[ADR:[^ ]+]] = bitcast i8* %[[CUR]] to %struct.tiny* |
159 | // CHECK: br |
160 | case 't': |
161 | s += va_arg(ap, struct tiny).a; |
162 | break; |
163 | |
164 | // CHECK: %[[CUR:[^ ]+]] = load i8*, i8** %ap |
165 | // CHECK-DAG: %[[NXT:[^ ]+]] = getelementptr inbounds i8, i8* %[[CUR]], i64 16 |
166 | // CHECK-DAG: store i8* %[[NXT]], i8** %ap |
167 | // CHECK-DAG: %[[ADR:[^ ]+]] = bitcast i8* %[[CUR]] to %struct.small* |
168 | // CHECK: br |
169 | case 's': |
170 | s += *va_arg(ap, struct small).a; |
171 | break; |
172 | |
173 | // CHECK: %[[CUR:[^ ]+]] = load i8*, i8** %ap |
174 | // CHECK-DAG: %[[NXT:[^ ]+]] = getelementptr inbounds i8, i8* %[[CUR]], i64 8 |
175 | // CHECK-DAG: store i8* %[[NXT]], i8** %ap |
176 | // CHECK-DAG: %[[IND:[^ ]+]] = bitcast i8* %[[CUR]] to %struct.medium** |
177 | // CHECK-DAG: %[[ADR:[^ ]+]] = load %struct.medium*, %struct.medium** %[[IND]] |
178 | // CHECK: br |
179 | case 'm': |
180 | s += *va_arg(ap, struct medium).a; |
181 | break; |
182 | } |
183 | return s; |
184 | } |
185 | |