Skip to content

Commit a27f40e

Browse files
authored
[X86_64] Fix empty field error in vaarg of C++. (#101639)
Such struct types: ``` struct { struct{} a; long long b; }; stuct { struct{} a; double b; }; ``` For such structures, Lo is NoClass and Hi is Integer/SSE. And when this structure argument is passed, the high part is passed at offset 8 in memory. So we should do special handling for these types in EmitVAArg.Fix #79790 and fix #86371.
1 parent 3f9c9ac commit a27f40e

File tree

2 files changed

+303
-19
lines changed

2 files changed

+303
-19
lines changed

clang/lib/CodeGen/Targets/X86.cpp

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3124,26 +3124,63 @@ RValue X86_64ABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
31243124
CGF.Builder.CreateStore(V, CGF.Builder.CreateStructGEP(Tmp, 1));
31253125

31263126
RegAddr = Tmp.withElementType(LTy);
3127-
} else if (neededInt) {
3128-
RegAddr = Address(CGF.Builder.CreateGEP(CGF.Int8Ty, RegSaveArea, gp_offset),
3129-
LTy, CharUnits::fromQuantity(8));
3130-
3127+
} else if (neededInt || neededSSE == 1) {
31313128
// Copy to a temporary if necessary to ensure the appropriate alignment.
31323129
auto TInfo = getContext().getTypeInfoInChars(Ty);
31333130
uint64_t TySize = TInfo.Width.getQuantity();
31343131
CharUnits TyAlign = TInfo.Align;
3135-
3136-
// Copy into a temporary if the type is more aligned than the
3137-
// register save area.
3138-
if (TyAlign.getQuantity() > 8) {
3132+
llvm::Type *CoTy = nullptr;
3133+
if (AI.isDirect())
3134+
CoTy = AI.getCoerceToType();
3135+
3136+
llvm::Value *GpOrFpOffset = neededInt ? gp_offset : fp_offset;
3137+
uint64_t Alignment = neededInt ? 8 : 16;
3138+
uint64_t RegSize = neededInt ? neededInt * 8 : 16;
3139+
// There are two cases require special handling:
3140+
// 1)
3141+
// ```
3142+
// struct {
3143+
// struct {} a[8];
3144+
// int b;
3145+
// };
3146+
// ```
3147+
// The lower 8 bytes of the structure are not stored,
3148+
// so an 8-byte offset is needed when accessing the structure.
3149+
// 2)
3150+
// ```
3151+
// struct {
3152+
// long long a;
3153+
// struct {} b;
3154+
// };
3155+
// ```
3156+
// The stored size of this structure is smaller than its actual size,
3157+
// which may lead to reading past the end of the register save area.
3158+
if (CoTy && (AI.getDirectOffset() == 8 || RegSize < TySize)) {
31393159
Address Tmp = CGF.CreateMemTemp(Ty);
3140-
CGF.Builder.CreateMemCpy(Tmp, RegAddr, TySize, false);
3141-
RegAddr = Tmp;
3160+
llvm::Value *Addr =
3161+
CGF.Builder.CreateGEP(CGF.Int8Ty, RegSaveArea, GpOrFpOffset);
3162+
llvm::Value *Src = CGF.Builder.CreateAlignedLoad(CoTy, Addr, TyAlign);
3163+
llvm::Value *PtrOffset =
3164+
llvm::ConstantInt::get(CGF.Int32Ty, AI.getDirectOffset());
3165+
Address Dst = Address(
3166+
CGF.Builder.CreateGEP(CGF.Int8Ty, Tmp.getBasePointer(), PtrOffset),
3167+
LTy, TyAlign);
3168+
CGF.Builder.CreateStore(Src, Dst);
3169+
RegAddr = Tmp.withElementType(LTy);
3170+
} else {
3171+
RegAddr =
3172+
Address(CGF.Builder.CreateGEP(CGF.Int8Ty, RegSaveArea, GpOrFpOffset),
3173+
LTy, CharUnits::fromQuantity(Alignment));
3174+
3175+
// Copy into a temporary if the type is more aligned than the
3176+
// register save area.
3177+
if (neededInt && TyAlign.getQuantity() > 8) {
3178+
Address Tmp = CGF.CreateMemTemp(Ty);
3179+
CGF.Builder.CreateMemCpy(Tmp, RegAddr, TySize, false);
3180+
RegAddr = Tmp;
3181+
}
31423182
}
31433183

3144-
} else if (neededSSE == 1) {
3145-
RegAddr = Address(CGF.Builder.CreateGEP(CGF.Int8Ty, RegSaveArea, fp_offset),
3146-
LTy, CharUnits::fromQuantity(16));
31473184
} else {
31483185
assert(neededSSE == 2 && "Invalid number of needed registers!");
31493186
// SSE registers are spaced 16 bytes apart in the register save

clang/test/CodeGenCXX/x86_64-vaarg.cpp

Lines changed: 253 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ typedef struct {
2929
// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_S1:%.*]], align 8
3030
// CHECK-NEXT: [[Z_ADDR:%.*]] = alloca i32, align 4
3131
// CHECK-NEXT: [[LIST:%.*]] = alloca [1 x %struct.__va_list_tag], align 16
32+
// CHECK-NEXT: [[TMP:%.*]] = alloca [[STRUCT_S1]], align 8
3233
// CHECK-NEXT: store i32 [[Z:%.*]], ptr [[Z_ADDR]], align 4
3334
// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [1 x %struct.__va_list_tag], ptr [[LIST]], i64 0, i64 0
3435
// CHECK-NEXT: call void @llvm.va_start.p0(ptr [[ARRAYDECAY]])
@@ -41,8 +42,11 @@ typedef struct {
4142
// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[STRUCT___VA_LIST_TAG]], ptr [[ARRAYDECAY1]], i32 0, i32 3
4243
// CHECK-NEXT: [[REG_SAVE_AREA:%.*]] = load ptr, ptr [[TMP0]], align 16
4344
// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[REG_SAVE_AREA]], i32 [[FP_OFFSET]]
44-
// CHECK-NEXT: [[TMP2:%.*]] = add i32 [[FP_OFFSET]], 16
45-
// CHECK-NEXT: store i32 [[TMP2]], ptr [[FP_OFFSET_P]], align 4
45+
// CHECK-NEXT: [[TMP2:%.*]] = load double, ptr [[TMP1]], align 8
46+
// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[TMP]], i32 8
47+
// CHECK-NEXT: store double [[TMP2]], ptr [[TMP3]], align 8
48+
// CHECK-NEXT: [[TMP4:%.*]] = add i32 [[FP_OFFSET]], 16
49+
// CHECK-NEXT: store i32 [[TMP4]], ptr [[FP_OFFSET_P]], align 4
4650
// CHECK-NEXT: br label [[VAARG_END:%.*]]
4751
// CHECK: vaarg.in_mem:
4852
// CHECK-NEXT: [[OVERFLOW_ARG_AREA_P:%.*]] = getelementptr inbounds nuw [[STRUCT___VA_LIST_TAG]], ptr [[ARRAYDECAY1]], i32 0, i32 2
@@ -51,14 +55,257 @@ typedef struct {
5155
// CHECK-NEXT: store ptr [[OVERFLOW_ARG_AREA_NEXT]], ptr [[OVERFLOW_ARG_AREA_P]], align 8
5256
// CHECK-NEXT: br label [[VAARG_END]]
5357
// CHECK: vaarg.end:
54-
// CHECK-NEXT: [[VAARG_ADDR:%.*]] = phi ptr [ [[TMP1]], [[VAARG_IN_REG]] ], [ [[OVERFLOW_ARG_AREA]], [[VAARG_IN_MEM]] ]
58+
// CHECK-NEXT: [[VAARG_ADDR:%.*]] = phi ptr [ [[TMP]], [[VAARG_IN_REG]] ], [ [[OVERFLOW_ARG_AREA]], [[VAARG_IN_MEM]] ]
5559
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr align 8 [[VAARG_ADDR]], i64 16, i1 false)
56-
// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i8, ptr [[RETVAL]], i64 8
57-
// CHECK-NEXT: [[TMP4:%.*]] = load double, ptr [[TMP3]], align 8
58-
// CHECK-NEXT: ret double [[TMP4]]
60+
// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds i8, ptr [[RETVAL]], i64 8
61+
// CHECK-NEXT: [[TMP6:%.*]] = load double, ptr [[TMP5]], align 8
62+
// CHECK-NEXT: ret double [[TMP6]]
5963
//
6064
s1 f(int z, ...) {
6165
__builtin_va_list list;
6266
__builtin_va_start(list, z);
6367
return __builtin_va_arg(list, s1);
6468
}
69+
70+
typedef struct {
71+
struct{} a[5];
72+
float b;
73+
float c;
74+
} s2;
75+
76+
// CHECK-LABEL: @_Z2f2iz(
77+
// CHECK-NEXT: entry:
78+
// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_S2:%.*]], align 4
79+
// CHECK-NEXT: [[Z_ADDR:%.*]] = alloca i32, align 4
80+
// CHECK-NEXT: [[LIST:%.*]] = alloca [1 x %struct.__va_list_tag], align 16
81+
// CHECK-NEXT: [[TMP:%.*]] = alloca [[STRUCT_S2]], align 4
82+
// CHECK-NEXT: store i32 [[Z:%.*]], ptr [[Z_ADDR]], align 4
83+
// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [1 x %struct.__va_list_tag], ptr [[LIST]], i64 0, i64 0
84+
// CHECK-NEXT: call void @llvm.va_start.p0(ptr [[ARRAYDECAY]])
85+
// CHECK-NEXT: [[ARRAYDECAY1:%.*]] = getelementptr inbounds [1 x %struct.__va_list_tag], ptr [[LIST]], i64 0, i64 0
86+
// CHECK-NEXT: [[FP_OFFSET_P:%.*]] = getelementptr inbounds nuw [[STRUCT___VA_LIST_TAG:%.*]], ptr [[ARRAYDECAY1]], i32 0, i32 1
87+
// CHECK-NEXT: [[FP_OFFSET:%.*]] = load i32, ptr [[FP_OFFSET_P]], align 4
88+
// CHECK-NEXT: [[FITS_IN_FP:%.*]] = icmp ule i32 [[FP_OFFSET]], 160
89+
// CHECK-NEXT: br i1 [[FITS_IN_FP]], label [[VAARG_IN_REG:%.*]], label [[VAARG_IN_MEM:%.*]]
90+
// CHECK: vaarg.in_reg:
91+
// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[STRUCT___VA_LIST_TAG]], ptr [[ARRAYDECAY1]], i32 0, i32 3
92+
// CHECK-NEXT: [[REG_SAVE_AREA:%.*]] = load ptr, ptr [[TMP0]], align 16
93+
// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[REG_SAVE_AREA]], i32 [[FP_OFFSET]]
94+
// CHECK-NEXT: [[TMP2:%.*]] = load <2 x float>, ptr [[TMP1]], align 4
95+
// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[TMP]], i32 8
96+
// CHECK-NEXT: store <2 x float> [[TMP2]], ptr [[TMP3]], align 4
97+
// CHECK-NEXT: [[TMP4:%.*]] = add i32 [[FP_OFFSET]], 16
98+
// CHECK-NEXT: store i32 [[TMP4]], ptr [[FP_OFFSET_P]], align 4
99+
// CHECK-NEXT: br label [[VAARG_END:%.*]]
100+
// CHECK: vaarg.in_mem:
101+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA_P:%.*]] = getelementptr inbounds nuw [[STRUCT___VA_LIST_TAG]], ptr [[ARRAYDECAY1]], i32 0, i32 2
102+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA:%.*]] = load ptr, ptr [[OVERFLOW_ARG_AREA_P]], align 8
103+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA_NEXT:%.*]] = getelementptr i8, ptr [[OVERFLOW_ARG_AREA]], i32 16
104+
// CHECK-NEXT: store ptr [[OVERFLOW_ARG_AREA_NEXT]], ptr [[OVERFLOW_ARG_AREA_P]], align 8
105+
// CHECK-NEXT: br label [[VAARG_END]]
106+
// CHECK: vaarg.end:
107+
// CHECK-NEXT: [[VAARG_ADDR:%.*]] = phi ptr [ [[TMP]], [[VAARG_IN_REG]] ], [ [[OVERFLOW_ARG_AREA]], [[VAARG_IN_MEM]] ]
108+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[VAARG_ADDR]], i64 16, i1 false)
109+
// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds i8, ptr [[RETVAL]], i64 8
110+
// CHECK-NEXT: [[TMP6:%.*]] = load <2 x float>, ptr [[TMP5]], align 4
111+
// CHECK-NEXT: ret <2 x float> [[TMP6]]
112+
//
113+
s2 f2(int z, ...) {
114+
__builtin_va_list list;
115+
__builtin_va_start(list, z);
116+
return __builtin_va_arg(list, s2);
117+
}
118+
119+
typedef struct {
120+
struct{} a;
121+
long long b;
122+
} s3;
123+
124+
// CHECK-LABEL: @_Z2f3iz(
125+
// CHECK-NEXT: entry:
126+
// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_S3:%.*]], align 8
127+
// CHECK-NEXT: [[Z_ADDR:%.*]] = alloca i32, align 4
128+
// CHECK-NEXT: [[LIST:%.*]] = alloca [1 x %struct.__va_list_tag], align 16
129+
// CHECK-NEXT: [[TMP:%.*]] = alloca [[STRUCT_S3]], align 8
130+
// CHECK-NEXT: store i32 [[Z:%.*]], ptr [[Z_ADDR]], align 4
131+
// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [1 x %struct.__va_list_tag], ptr [[LIST]], i64 0, i64 0
132+
// CHECK-NEXT: call void @llvm.va_start.p0(ptr [[ARRAYDECAY]])
133+
// CHECK-NEXT: [[ARRAYDECAY1:%.*]] = getelementptr inbounds [1 x %struct.__va_list_tag], ptr [[LIST]], i64 0, i64 0
134+
// CHECK-NEXT: [[GP_OFFSET_P:%.*]] = getelementptr inbounds nuw [[STRUCT___VA_LIST_TAG:%.*]], ptr [[ARRAYDECAY1]], i32 0, i32 0
135+
// CHECK-NEXT: [[GP_OFFSET:%.*]] = load i32, ptr [[GP_OFFSET_P]], align 16
136+
// CHECK-NEXT: [[FITS_IN_GP:%.*]] = icmp ule i32 [[GP_OFFSET]], 40
137+
// CHECK-NEXT: br i1 [[FITS_IN_GP]], label [[VAARG_IN_REG:%.*]], label [[VAARG_IN_MEM:%.*]]
138+
// CHECK: vaarg.in_reg:
139+
// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[STRUCT___VA_LIST_TAG]], ptr [[ARRAYDECAY1]], i32 0, i32 3
140+
// CHECK-NEXT: [[REG_SAVE_AREA:%.*]] = load ptr, ptr [[TMP0]], align 16
141+
// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[REG_SAVE_AREA]], i32 [[GP_OFFSET]]
142+
// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[TMP1]], align 8
143+
// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[TMP]], i32 8
144+
// CHECK-NEXT: store i64 [[TMP2]], ptr [[TMP3]], align 8
145+
// CHECK-NEXT: [[TMP4:%.*]] = add i32 [[GP_OFFSET]], 8
146+
// CHECK-NEXT: store i32 [[TMP4]], ptr [[GP_OFFSET_P]], align 16
147+
// CHECK-NEXT: br label [[VAARG_END:%.*]]
148+
// CHECK: vaarg.in_mem:
149+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA_P:%.*]] = getelementptr inbounds nuw [[STRUCT___VA_LIST_TAG]], ptr [[ARRAYDECAY1]], i32 0, i32 2
150+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA:%.*]] = load ptr, ptr [[OVERFLOW_ARG_AREA_P]], align 8
151+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA_NEXT:%.*]] = getelementptr i8, ptr [[OVERFLOW_ARG_AREA]], i32 16
152+
// CHECK-NEXT: store ptr [[OVERFLOW_ARG_AREA_NEXT]], ptr [[OVERFLOW_ARG_AREA_P]], align 8
153+
// CHECK-NEXT: br label [[VAARG_END]]
154+
// CHECK: vaarg.end:
155+
// CHECK-NEXT: [[VAARG_ADDR:%.*]] = phi ptr [ [[TMP]], [[VAARG_IN_REG]] ], [ [[OVERFLOW_ARG_AREA]], [[VAARG_IN_MEM]] ]
156+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr align 8 [[VAARG_ADDR]], i64 16, i1 false)
157+
// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds i8, ptr [[RETVAL]], i64 8
158+
// CHECK-NEXT: [[TMP6:%.*]] = load i64, ptr [[TMP5]], align 8
159+
// CHECK-NEXT: ret i64 [[TMP6]]
160+
//
161+
s3 f3(int z, ...) {
162+
__builtin_va_list list;
163+
__builtin_va_start(list, z);
164+
return __builtin_va_arg(list, s3);
165+
}
166+
167+
typedef struct {
168+
struct{} a[7];
169+
short b;
170+
int c;
171+
} s4;
172+
173+
// CHECK-LABEL: @_Z2f4iz(
174+
// CHECK-NEXT: entry:
175+
// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_S4:%.*]], align 4
176+
// CHECK-NEXT: [[Z_ADDR:%.*]] = alloca i32, align 4
177+
// CHECK-NEXT: [[LIST:%.*]] = alloca [1 x %struct.__va_list_tag], align 16
178+
// CHECK-NEXT: [[TMP:%.*]] = alloca [[STRUCT_S4]], align 4
179+
// CHECK-NEXT: store i32 [[Z:%.*]], ptr [[Z_ADDR]], align 4
180+
// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [1 x %struct.__va_list_tag], ptr [[LIST]], i64 0, i64 0
181+
// CHECK-NEXT: call void @llvm.va_start.p0(ptr [[ARRAYDECAY]])
182+
// CHECK-NEXT: [[ARRAYDECAY1:%.*]] = getelementptr inbounds [1 x %struct.__va_list_tag], ptr [[LIST]], i64 0, i64 0
183+
// CHECK-NEXT: [[GP_OFFSET_P:%.*]] = getelementptr inbounds nuw [[STRUCT___VA_LIST_TAG:%.*]], ptr [[ARRAYDECAY1]], i32 0, i32 0
184+
// CHECK-NEXT: [[GP_OFFSET:%.*]] = load i32, ptr [[GP_OFFSET_P]], align 16
185+
// CHECK-NEXT: [[FITS_IN_GP:%.*]] = icmp ule i32 [[GP_OFFSET]], 40
186+
// CHECK-NEXT: br i1 [[FITS_IN_GP]], label [[VAARG_IN_REG:%.*]], label [[VAARG_IN_MEM:%.*]]
187+
// CHECK: vaarg.in_reg:
188+
// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[STRUCT___VA_LIST_TAG]], ptr [[ARRAYDECAY1]], i32 0, i32 3
189+
// CHECK-NEXT: [[REG_SAVE_AREA:%.*]] = load ptr, ptr [[TMP0]], align 16
190+
// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[REG_SAVE_AREA]], i32 [[GP_OFFSET]]
191+
// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[TMP1]], align 4
192+
// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[TMP]], i32 8
193+
// CHECK-NEXT: store i64 [[TMP2]], ptr [[TMP3]], align 4
194+
// CHECK-NEXT: [[TMP4:%.*]] = add i32 [[GP_OFFSET]], 8
195+
// CHECK-NEXT: store i32 [[TMP4]], ptr [[GP_OFFSET_P]], align 16
196+
// CHECK-NEXT: br label [[VAARG_END:%.*]]
197+
// CHECK: vaarg.in_mem:
198+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA_P:%.*]] = getelementptr inbounds nuw [[STRUCT___VA_LIST_TAG]], ptr [[ARRAYDECAY1]], i32 0, i32 2
199+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA:%.*]] = load ptr, ptr [[OVERFLOW_ARG_AREA_P]], align 8
200+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA_NEXT:%.*]] = getelementptr i8, ptr [[OVERFLOW_ARG_AREA]], i32 16
201+
// CHECK-NEXT: store ptr [[OVERFLOW_ARG_AREA_NEXT]], ptr [[OVERFLOW_ARG_AREA_P]], align 8
202+
// CHECK-NEXT: br label [[VAARG_END]]
203+
// CHECK: vaarg.end:
204+
// CHECK-NEXT: [[VAARG_ADDR:%.*]] = phi ptr [ [[TMP]], [[VAARG_IN_REG]] ], [ [[OVERFLOW_ARG_AREA]], [[VAARG_IN_MEM]] ]
205+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[VAARG_ADDR]], i64 16, i1 false)
206+
// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds i8, ptr [[RETVAL]], i64 8
207+
// CHECK-NEXT: [[TMP6:%.*]] = load i64, ptr [[TMP5]], align 4
208+
// CHECK-NEXT: ret i64 [[TMP6]]
209+
//
210+
s4 f4(int z, ...) {
211+
__builtin_va_list list;
212+
__builtin_va_start(list, z);
213+
return __builtin_va_arg(list, s4);
214+
}
215+
216+
typedef struct {
217+
struct{} a[5];
218+
float b;
219+
int c;
220+
} s5;
221+
222+
// CHECK-LABEL: @_Z2f5iz(
223+
// CHECK-NEXT: entry:
224+
// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_S5:%.*]], align 4
225+
// CHECK-NEXT: [[Z_ADDR:%.*]] = alloca i32, align 4
226+
// CHECK-NEXT: [[LIST:%.*]] = alloca [1 x %struct.__va_list_tag], align 16
227+
// CHECK-NEXT: [[TMP:%.*]] = alloca [[STRUCT_S5]], align 4
228+
// CHECK-NEXT: store i32 [[Z:%.*]], ptr [[Z_ADDR]], align 4
229+
// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [1 x %struct.__va_list_tag], ptr [[LIST]], i64 0, i64 0
230+
// CHECK-NEXT: call void @llvm.va_start.p0(ptr [[ARRAYDECAY]])
231+
// CHECK-NEXT: [[ARRAYDECAY1:%.*]] = getelementptr inbounds [1 x %struct.__va_list_tag], ptr [[LIST]], i64 0, i64 0
232+
// CHECK-NEXT: [[GP_OFFSET_P:%.*]] = getelementptr inbounds nuw [[STRUCT___VA_LIST_TAG:%.*]], ptr [[ARRAYDECAY1]], i32 0, i32 0
233+
// CHECK-NEXT: [[GP_OFFSET:%.*]] = load i32, ptr [[GP_OFFSET_P]], align 16
234+
// CHECK-NEXT: [[FITS_IN_GP:%.*]] = icmp ule i32 [[GP_OFFSET]], 40
235+
// CHECK-NEXT: br i1 [[FITS_IN_GP]], label [[VAARG_IN_REG:%.*]], label [[VAARG_IN_MEM:%.*]]
236+
// CHECK: vaarg.in_reg:
237+
// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[STRUCT___VA_LIST_TAG]], ptr [[ARRAYDECAY1]], i32 0, i32 3
238+
// CHECK-NEXT: [[REG_SAVE_AREA:%.*]] = load ptr, ptr [[TMP0]], align 16
239+
// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[REG_SAVE_AREA]], i32 [[GP_OFFSET]]
240+
// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[TMP1]], align 4
241+
// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[TMP]], i32 8
242+
// CHECK-NEXT: store i64 [[TMP2]], ptr [[TMP3]], align 4
243+
// CHECK-NEXT: [[TMP4:%.*]] = add i32 [[GP_OFFSET]], 8
244+
// CHECK-NEXT: store i32 [[TMP4]], ptr [[GP_OFFSET_P]], align 16
245+
// CHECK-NEXT: br label [[VAARG_END:%.*]]
246+
// CHECK: vaarg.in_mem:
247+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA_P:%.*]] = getelementptr inbounds nuw [[STRUCT___VA_LIST_TAG]], ptr [[ARRAYDECAY1]], i32 0, i32 2
248+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA:%.*]] = load ptr, ptr [[OVERFLOW_ARG_AREA_P]], align 8
249+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA_NEXT:%.*]] = getelementptr i8, ptr [[OVERFLOW_ARG_AREA]], i32 16
250+
// CHECK-NEXT: store ptr [[OVERFLOW_ARG_AREA_NEXT]], ptr [[OVERFLOW_ARG_AREA_P]], align 8
251+
// CHECK-NEXT: br label [[VAARG_END]]
252+
// CHECK: vaarg.end:
253+
// CHECK-NEXT: [[VAARG_ADDR:%.*]] = phi ptr [ [[TMP]], [[VAARG_IN_REG]] ], [ [[OVERFLOW_ARG_AREA]], [[VAARG_IN_MEM]] ]
254+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[VAARG_ADDR]], i64 16, i1 false)
255+
// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds i8, ptr [[RETVAL]], i64 8
256+
// CHECK-NEXT: [[TMP6:%.*]] = load i64, ptr [[TMP5]], align 4
257+
// CHECK-NEXT: ret i64 [[TMP6]]
258+
//
259+
s5 f5(int z, ...) {
260+
__builtin_va_list list;
261+
__builtin_va_start(list, z);
262+
return __builtin_va_arg(list, s5);
263+
}
264+
265+
typedef struct {
266+
long long a;
267+
struct{} b;
268+
} s6;
269+
270+
// CHECK-LABEL: @_Z2f6iz(
271+
// CHECK-NEXT: entry:
272+
// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_S6:%.*]], align 8
273+
// CHECK-NEXT: [[Z_ADDR:%.*]] = alloca i32, align 4
274+
// CHECK-NEXT: [[LIST:%.*]] = alloca [1 x %struct.__va_list_tag], align 16
275+
// CHECK-NEXT: [[TMP:%.*]] = alloca [[STRUCT_S6]], align 8
276+
// CHECK-NEXT: store i32 [[Z:%.*]], ptr [[Z_ADDR]], align 4
277+
// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [1 x %struct.__va_list_tag], ptr [[LIST]], i64 0, i64 0
278+
// CHECK-NEXT: call void @llvm.va_start.p0(ptr [[ARRAYDECAY]])
279+
// CHECK-NEXT: [[ARRAYDECAY1:%.*]] = getelementptr inbounds [1 x %struct.__va_list_tag], ptr [[LIST]], i64 0, i64 0
280+
// CHECK-NEXT: [[GP_OFFSET_P:%.*]] = getelementptr inbounds nuw [[STRUCT___VA_LIST_TAG:%.*]], ptr [[ARRAYDECAY1]], i32 0, i32 0
281+
// CHECK-NEXT: [[GP_OFFSET:%.*]] = load i32, ptr [[GP_OFFSET_P]], align 16
282+
// CHECK-NEXT: [[FITS_IN_GP:%.*]] = icmp ule i32 [[GP_OFFSET]], 40
283+
// CHECK-NEXT: br i1 [[FITS_IN_GP]], label [[VAARG_IN_REG:%.*]], label [[VAARG_IN_MEM:%.*]]
284+
// CHECK: vaarg.in_reg:
285+
// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[STRUCT___VA_LIST_TAG]], ptr [[ARRAYDECAY1]], i32 0, i32 3
286+
// CHECK-NEXT: [[REG_SAVE_AREA:%.*]] = load ptr, ptr [[TMP0]], align 16
287+
// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[REG_SAVE_AREA]], i32 [[GP_OFFSET]]
288+
// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[TMP1]], align 8
289+
// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[TMP]], i32 0
290+
// CHECK-NEXT: store i64 [[TMP2]], ptr [[TMP3]], align 8
291+
// CHECK-NEXT: [[TMP4:%.*]] = add i32 [[GP_OFFSET]], 8
292+
// CHECK-NEXT: store i32 [[TMP4]], ptr [[GP_OFFSET_P]], align 16
293+
// CHECK-NEXT: br label [[VAARG_END:%.*]]
294+
// CHECK: vaarg.in_mem:
295+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA_P:%.*]] = getelementptr inbounds nuw [[STRUCT___VA_LIST_TAG]], ptr [[ARRAYDECAY1]], i32 0, i32 2
296+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA:%.*]] = load ptr, ptr [[OVERFLOW_ARG_AREA_P]], align 8
297+
// CHECK-NEXT: [[OVERFLOW_ARG_AREA_NEXT:%.*]] = getelementptr i8, ptr [[OVERFLOW_ARG_AREA]], i32 16
298+
// CHECK-NEXT: store ptr [[OVERFLOW_ARG_AREA_NEXT]], ptr [[OVERFLOW_ARG_AREA_P]], align 8
299+
// CHECK-NEXT: br label [[VAARG_END]]
300+
// CHECK: vaarg.end:
301+
// CHECK-NEXT: [[VAARG_ADDR:%.*]] = phi ptr [ [[TMP]], [[VAARG_IN_REG]] ], [ [[OVERFLOW_ARG_AREA]], [[VAARG_IN_MEM]] ]
302+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr align 8 [[VAARG_ADDR]], i64 16, i1 false)
303+
// CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_S6]], ptr [[RETVAL]], i32 0, i32 0
304+
// CHECK-NEXT: [[TMP5:%.*]] = load i64, ptr [[COERCE_DIVE]], align 8
305+
// CHECK-NEXT: ret i64 [[TMP5]]
306+
//
307+
s6 f6(int z, ...) {
308+
__builtin_va_list list;
309+
__builtin_va_start(list, z);
310+
return __builtin_va_arg(list, s6);
311+
}

0 commit comments

Comments
 (0)