Skip to content

Commit bd11e64

Browse files
[WIP] Expand variadic functions in IR
1 parent 78eb2c2 commit bd11e64

36 files changed

+5930
-24
lines changed

clang/lib/CodeGen/ABIInfoImpl.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ llvm::Value *CodeGen::emitRoundPointerUpToAlignment(CodeGenFunction &CGF,
157157
llvm::Value *RoundUp = CGF.Builder.CreateConstInBoundsGEP1_32(
158158
CGF.Builder.getInt8Ty(), Ptr, Align.getQuantity() - 1);
159159
return CGF.Builder.CreateIntrinsic(
160-
llvm::Intrinsic::ptrmask, {Ptr->getType(), CGF.IntPtrTy},
160+
llvm::Intrinsic::ptrmask, {Ptr->getType(), CGF.IntPtrTy},
161161
{RoundUp, llvm::ConstantInt::get(CGF.IntPtrTy, -Align.getQuantity())},
162162
nullptr, Ptr->getName() + ".aligned");
163163
}
@@ -247,7 +247,7 @@ Address CodeGen::emitMergePHI(CodeGenFunction &CGF, Address Addr1,
247247

248248
bool CodeGen::isEmptyField(ASTContext &Context, const FieldDecl *FD,
249249
bool AllowArrays, bool AsIfNoUniqueAddr) {
250-
if (FD->isUnnamedBitField())
250+
if (FD->isUnnamedBitfield())
251251
return true;
252252

253253
QualType FT = FD->getType();

clang/lib/CodeGen/Targets/AMDGPU.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,13 @@ void AMDGPUABIInfo::computeInfo(CGFunctionInfo &FI) const {
115115

116116
Address AMDGPUABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
117117
QualType Ty) const {
118-
llvm_unreachable("AMDGPU does not support varargs");
118+
const bool IsIndirect = false;
119+
const bool AllowHigherAlign = true;
120+
// Would rather not naturally align values
121+
// Splitting {char, short} into two separate arguments makes that difficult.
122+
return emitVoidPtrVAArg(CGF, VAListAddr, Ty, IsIndirect,
123+
getContext().getTypeInfoInChars(Ty),
124+
CharUnits::fromQuantity(1), AllowHigherAlign);
119125
}
120126

121127
ABIArgInfo AMDGPUABIInfo::classifyReturnType(QualType RetTy) const {

clang/lib/CodeGen/Targets/NVPTX.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,13 @@ void NVPTXABIInfo::computeInfo(CGFunctionInfo &FI) const {
215215

216216
Address NVPTXABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
217217
QualType Ty) const {
218-
llvm_unreachable("NVPTX does not support varargs");
218+
// TODO: Work out to what extent this correlates with ptx
219+
// doubles get passed with 8 byte alignment and C promotes smaller integer
220+
// types to int. Printf doesn't really do structs so hard to guess what
221+
// the right thing is for that.
222+
return emitVoidPtrVAArg(CGF, VAListAddr, Ty, false,
223+
getContext().getTypeInfoInChars(Ty),
224+
CharUnits::fromQuantity(4), true);
219225
}
220226

221227
void NVPTXTargetCodeGenInfo::setTargetAttributes(
Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
2+
3+
// REQUIRES: x86-registered-target
4+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -target-cpu x86-64-v4 -std=c23 -O1 -ffreestanding -mllvm --expand-variadics-override=disable -emit-llvm -o - %s | FileCheck %s
5+
6+
// This test sanity checks calling a variadic function with the expansion transform disabled.
7+
// The IR test cases {arch}/expand-variadic-call-*.ll correspond to IR generated from this test case.
8+
9+
typedef __builtin_va_list va_list;
10+
#define va_copy(dest, src) __builtin_va_copy(dest, src)
11+
#define va_start(ap, ...) __builtin_va_start(ap, 0)
12+
#define va_end(ap) __builtin_va_end(ap)
13+
#define va_arg(ap, type) __builtin_va_arg(ap, type)
14+
15+
// 32 bit x86 alignment uses getTypeStackAlign for special cases
16+
// Whitebox testing.
17+
// Needs a type >= 16 which is either a simd or a struct containing a simd
18+
// darwinvectorabi should force 4 bytes
19+
// linux vectors with align 16/32/64 return that alignment
20+
21+
22+
// Might want various copy/end style constructs in a separate test
23+
24+
void vararg(...);
25+
void valist(va_list);
26+
27+
// CHECK-LABEL: @copy(
28+
// CHECK-NEXT: entry:
29+
// CHECK-NEXT: [[CP:%.*]] = alloca [1 x %struct.__va_list_tag], align 16
30+
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[CP]]) #[[ATTR7:[0-9]+]]
31+
// CHECK-NEXT: call void @llvm.va_copy.p0(ptr nonnull [[CP]], ptr [[VA:%.*]])
32+
// CHECK-NEXT: call void @valist(ptr noundef nonnull [[CP]]) #[[ATTR8:[0-9]+]]
33+
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[CP]]) #[[ATTR7]]
34+
// CHECK-NEXT: ret void
35+
//
36+
void copy(va_list va)
37+
{
38+
va_list cp;
39+
va_copy(cp, va);
40+
valist(cp);
41+
}
42+
43+
// CHECK-LABEL: @start_once(
44+
// CHECK-NEXT: entry:
45+
// CHECK-NEXT: [[S:%.*]] = alloca [1 x %struct.__va_list_tag], align 16
46+
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[S]]) #[[ATTR7]]
47+
// CHECK-NEXT: call void @llvm.va_start.p0(ptr nonnull [[S]])
48+
// CHECK-NEXT: call void @valist(ptr noundef nonnull [[S]]) #[[ATTR8]]
49+
// CHECK-NEXT: call void @llvm.va_end.p0(ptr [[S]])
50+
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[S]]) #[[ATTR7]]
51+
// CHECK-NEXT: ret void
52+
//
53+
void start_once(...)
54+
{
55+
va_list s;
56+
va_start(s);
57+
valist(s);
58+
va_end(s);
59+
}
60+
61+
// CHECK-LABEL: @start_twice(
62+
// CHECK-NEXT: entry:
63+
// CHECK-NEXT: [[S0:%.*]] = alloca [1 x %struct.__va_list_tag], align 16
64+
// CHECK-NEXT: [[S1:%.*]] = alloca [1 x %struct.__va_list_tag], align 16
65+
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[S0]]) #[[ATTR7]]
66+
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[S1]]) #[[ATTR7]]
67+
// CHECK-NEXT: call void @llvm.va_start.p0(ptr nonnull [[S0]])
68+
// CHECK-NEXT: call void @valist(ptr noundef nonnull [[S0]]) #[[ATTR8]]
69+
// CHECK-NEXT: call void @llvm.va_end.p0(ptr [[S0]])
70+
// CHECK-NEXT: call void @llvm.va_start.p0(ptr nonnull [[S1]])
71+
// CHECK-NEXT: call void @valist(ptr noundef nonnull [[S1]]) #[[ATTR8]]
72+
// CHECK-NEXT: call void @llvm.va_end.p0(ptr [[S1]])
73+
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[S1]]) #[[ATTR7]]
74+
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[S0]]) #[[ATTR7]]
75+
// CHECK-NEXT: ret void
76+
//
77+
void start_twice(...)
78+
{
79+
va_list s0,s1;
80+
va_start(s0);
81+
valist(s0);
82+
va_end(s0);
83+
va_start(s1);
84+
valist(s1);
85+
va_end(s1);
86+
}
87+
88+
// vectors with alignment 16/32/64 are natively aligned on linux x86
89+
// v32f32 would be a m1024 type, larger than x64 defines at time of writing
90+
typedef int i32;
91+
typedef float v4f32 __attribute__((__vector_size__(16), __aligned__(16)));
92+
typedef float v8f32 __attribute__((__vector_size__(32), __aligned__(32)));
93+
typedef float v16f32 __attribute__((__vector_size__(64), __aligned__(64)));
94+
typedef float v32f32 __attribute__((__vector_size__(128), __aligned__(128)));
95+
96+
97+
// Pass a single value to wrapped() via vararg(...)
98+
// CHECK-LABEL: @single_i32(
99+
// CHECK-NEXT: entry:
100+
// CHECK-NEXT: tail call void (...) @vararg(i32 noundef [[X:%.*]]) #[[ATTR8]]
101+
// CHECK-NEXT: ret void
102+
//
103+
void single_i32(i32 x)
104+
{
105+
vararg(x);
106+
}
107+
108+
109+
// CHECK-LABEL: @single_double(
110+
// CHECK-NEXT: entry:
111+
// CHECK-NEXT: tail call void (...) @vararg(double noundef [[X:%.*]]) #[[ATTR8]]
112+
// CHECK-NEXT: ret void
113+
//
114+
void single_double(double x)
115+
{
116+
vararg(x);
117+
}
118+
119+
// CHECK-LABEL: @single_v4f32(
120+
// CHECK-NEXT: entry:
121+
// CHECK-NEXT: tail call void (...) @vararg(<4 x float> noundef [[X:%.*]]) #[[ATTR8]]
122+
// CHECK-NEXT: ret void
123+
//
124+
void single_v4f32(v4f32 x)
125+
{
126+
vararg(x);
127+
}
128+
129+
// CHECK-LABEL: @single_v8f32(
130+
// CHECK-NEXT: entry:
131+
// CHECK-NEXT: tail call void (...) @vararg(<8 x float> noundef [[X:%.*]]) #[[ATTR8]]
132+
// CHECK-NEXT: ret void
133+
//
134+
void single_v8f32(v8f32 x)
135+
{
136+
vararg(x);
137+
}
138+
139+
// CHECK-LABEL: @single_v16f32(
140+
// CHECK-NEXT: entry:
141+
// CHECK-NEXT: tail call void (...) @vararg(<16 x float> noundef [[X:%.*]]) #[[ATTR8]]
142+
// CHECK-NEXT: ret void
143+
//
144+
void single_v16f32(v16f32 x)
145+
{
146+
vararg(x);
147+
}
148+
149+
// CHECK-LABEL: @single_v32f32(
150+
// CHECK-NEXT: entry:
151+
// CHECK-NEXT: [[INDIRECT_ARG_TEMP:%.*]] = alloca <32 x float>, align 128
152+
// CHECK-NEXT: [[X:%.*]] = load <32 x float>, ptr [[TMP0:%.*]], align 128, !tbaa [[TBAA2:![0-9]+]]
153+
// CHECK-NEXT: store <32 x float> [[X]], ptr [[INDIRECT_ARG_TEMP]], align 128, !tbaa [[TBAA2]]
154+
// CHECK-NEXT: tail call void (...) @vararg(ptr noundef nonnull byval(<32 x float>) align 128 [[INDIRECT_ARG_TEMP]]) #[[ATTR8]]
155+
// CHECK-NEXT: ret void
156+
//
157+
void single_v32f32(v32f32 x)
158+
{
159+
vararg(x);
160+
}
161+
162+
163+
164+
// CHECK-LABEL: @i32_double(
165+
// CHECK-NEXT: entry:
166+
// CHECK-NEXT: tail call void (...) @vararg(i32 noundef [[X:%.*]], double noundef [[Y:%.*]]) #[[ATTR8]]
167+
// CHECK-NEXT: ret void
168+
//
169+
void i32_double(i32 x, double y)
170+
{
171+
vararg(x, y);
172+
}
173+
174+
// CHECK-LABEL: @double_i32(
175+
// CHECK-NEXT: entry:
176+
// CHECK-NEXT: tail call void (...) @vararg(double noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ATTR8]]
177+
// CHECK-NEXT: ret void
178+
//
179+
void double_i32(double x, i32 y)
180+
{
181+
vararg(x, y);
182+
}
183+
184+
185+
// A struct used by libc variadic tests
186+
187+
typedef struct {
188+
char c;
189+
short s;
190+
int i;
191+
long l;
192+
float f;
193+
double d;
194+
} libcS;
195+
196+
// CHECK-LABEL: @i32_libcS(
197+
// CHECK-NEXT: entry:
198+
// CHECK-NEXT: tail call void (...) @vararg(i32 noundef [[X:%.*]], ptr noundef nonnull byval([[STRUCT_LIBCS:%.*]]) align 8 [[Y:%.*]]) #[[ATTR8]]
199+
// CHECK-NEXT: ret void
200+
//
201+
void i32_libcS(i32 x, libcS y)
202+
{
203+
vararg(x, y);
204+
}
205+
206+
// CHECK-LABEL: @libcS_i32(
207+
// CHECK-NEXT: entry:
208+
// CHECK-NEXT: tail call void (...) @vararg(ptr noundef nonnull byval([[STRUCT_LIBCS:%.*]]) align 8 [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ATTR8]]
209+
// CHECK-NEXT: ret void
210+
//
211+
void libcS_i32(libcS x, i32 y)
212+
{
213+
vararg(x, y);
214+
}
215+
216+
217+
// CHECK-LABEL: @i32_v4f32(
218+
// CHECK-NEXT: entry:
219+
// CHECK-NEXT: tail call void (...) @vararg(i32 noundef [[X:%.*]], <4 x float> noundef [[Y:%.*]]) #[[ATTR8]]
220+
// CHECK-NEXT: ret void
221+
//
222+
void i32_v4f32(i32 x, v4f32 y)
223+
{
224+
vararg(x, y);
225+
}
226+
227+
// CHECK-LABEL: @v4f32_i32(
228+
// CHECK-NEXT: entry:
229+
// CHECK-NEXT: tail call void (...) @vararg(<4 x float> noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ATTR8]]
230+
// CHECK-NEXT: ret void
231+
//
232+
void v4f32_i32(v4f32 x, i32 y)
233+
{
234+
vararg(x, y);
235+
}
236+
237+
// CHECK-LABEL: @i32_v8f32(
238+
// CHECK-NEXT: entry:
239+
// CHECK-NEXT: tail call void (...) @vararg(i32 noundef [[X:%.*]], <8 x float> noundef [[Y:%.*]]) #[[ATTR8]]
240+
// CHECK-NEXT: ret void
241+
//
242+
void i32_v8f32(i32 x, v8f32 y)
243+
{
244+
vararg(x, y);
245+
}
246+
247+
// CHECK-LABEL: @v8f32_i32(
248+
// CHECK-NEXT: entry:
249+
// CHECK-NEXT: tail call void (...) @vararg(<8 x float> noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ATTR8]]
250+
// CHECK-NEXT: ret void
251+
//
252+
void v8f32_i32(v8f32 x, i32 y)
253+
{
254+
vararg(x, y);
255+
}
256+
257+
// CHECK-LABEL: @i32_v16f32(
258+
// CHECK-NEXT: entry:
259+
// CHECK-NEXT: tail call void (...) @vararg(i32 noundef [[X:%.*]], <16 x float> noundef [[Y:%.*]]) #[[ATTR8]]
260+
// CHECK-NEXT: ret void
261+
//
262+
void i32_v16f32(i32 x, v16f32 y)
263+
{
264+
vararg(x, y);
265+
}
266+
267+
// CHECK-LABEL: @v16f32_i32(
268+
// CHECK-NEXT: entry:
269+
// CHECK-NEXT: tail call void (...) @vararg(<16 x float> noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ATTR8]]
270+
// CHECK-NEXT: ret void
271+
//
272+
void v16f32_i32(v16f32 x, i32 y)
273+
{
274+
vararg(x, y);
275+
}
276+
277+
// CHECK-LABEL: @i32_v32f32(
278+
// CHECK-NEXT: entry:
279+
// CHECK-NEXT: [[INDIRECT_ARG_TEMP:%.*]] = alloca <32 x float>, align 128
280+
// CHECK-NEXT: [[Y:%.*]] = load <32 x float>, ptr [[TMP0:%.*]], align 128, !tbaa [[TBAA2]]
281+
// CHECK-NEXT: store <32 x float> [[Y]], ptr [[INDIRECT_ARG_TEMP]], align 128, !tbaa [[TBAA2]]
282+
// CHECK-NEXT: tail call void (...) @vararg(i32 noundef [[X:%.*]], ptr noundef nonnull byval(<32 x float>) align 128 [[INDIRECT_ARG_TEMP]]) #[[ATTR8]]
283+
// CHECK-NEXT: ret void
284+
//
285+
void i32_v32f32(i32 x, v32f32 y)
286+
{
287+
vararg(x, y);
288+
}
289+
290+
// CHECK-LABEL: @v32f32_i32(
291+
// CHECK-NEXT: entry:
292+
// CHECK-NEXT: [[INDIRECT_ARG_TEMP:%.*]] = alloca <32 x float>, align 128
293+
// CHECK-NEXT: [[X:%.*]] = load <32 x float>, ptr [[TMP0:%.*]], align 128, !tbaa [[TBAA2]]
294+
// CHECK-NEXT: store <32 x float> [[X]], ptr [[INDIRECT_ARG_TEMP]], align 128, !tbaa [[TBAA2]]
295+
// CHECK-NEXT: tail call void (...) @vararg(ptr noundef nonnull byval(<32 x float>) align 128 [[INDIRECT_ARG_TEMP]], i32 noundef [[Y:%.*]]) #[[ATTR8]]
296+
// CHECK-NEXT: ret void
297+
//
298+
void v32f32_i32(v32f32 x, i32 y)
299+
{
300+
vararg(x, y);
301+
}
302+
303+
#if __AMDGPU__ || __NVPTX__
304+
305+
306+
void (*volatile vararg_ptr)(...) = &vararg;
307+
308+
void indirect_single_i32(i32 x)
309+
{
310+
vararg_ptr(x);
311+
}
312+
313+
314+
#endif

0 commit comments

Comments
 (0)