Skip to content

Commit 9ee4ac8

Browse files
koachanbrad0
authored andcommitted
[clang][SPARC] Pass 16-aligned structs with the correct alignment in CC (llvm#155829)
Pad argument registers to preserve overaligned structs in LLVM IR. Additionally, since i128 values will be lowered as split i64 pairs in the backend, correctly set the alignment of such arguments as 16 bytes. This should make clang compliant with the ABI specification and fix llvm#144709. (cherry picked from commit 6679e43)
1 parent 220bac1 commit 9ee4ac8

File tree

4 files changed

+129
-70
lines changed

4 files changed

+129
-70
lines changed

clang/lib/CodeGen/Targets/Sparc.cpp

Lines changed: 57 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "ABIInfoImpl.h"
1010
#include "TargetInfo.h"
11+
#include <algorithm>
1112

1213
using namespace clang;
1314
using namespace clang::CodeGen;
@@ -109,7 +110,8 @@ class SparcV9ABIInfo : public ABIInfo {
109110
SparcV9ABIInfo(CodeGenTypes &CGT) : ABIInfo(CGT) {}
110111

111112
private:
112-
ABIArgInfo classifyType(QualType RetTy, unsigned SizeLimit) const;
113+
ABIArgInfo classifyType(QualType RetTy, unsigned SizeLimit,
114+
unsigned &RegOffset) const;
113115
void computeInfo(CGFunctionInfo &FI) const override;
114116
RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty,
115117
AggValueSlot Slot) const override;
@@ -222,127 +224,114 @@ class SparcV9ABIInfo : public ABIInfo {
222224
};
223225
} // end anonymous namespace
224226

225-
ABIArgInfo
226-
SparcV9ABIInfo::classifyType(QualType Ty, unsigned SizeLimit) const {
227+
ABIArgInfo SparcV9ABIInfo::classifyType(QualType Ty, unsigned SizeLimit,
228+
unsigned &RegOffset) const {
227229
if (Ty->isVoidType())
228230
return ABIArgInfo::getIgnore();
229231

230-
uint64_t Size = getContext().getTypeSize(Ty);
232+
auto &Context = getContext();
233+
auto &VMContext = getVMContext();
234+
235+
uint64_t Size = Context.getTypeSize(Ty);
236+
unsigned Alignment = Context.getTypeAlign(Ty);
237+
bool NeedPadding = (Alignment > 64) && (RegOffset % 2 != 0);
231238

232239
// Anything too big to fit in registers is passed with an explicit indirect
233240
// pointer / sret pointer.
234-
if (Size > SizeLimit)
241+
if (Size > SizeLimit) {
242+
RegOffset += 1;
235243
return getNaturalAlignIndirect(
236244
Ty, /*AddrSpace=*/getDataLayout().getAllocaAddrSpace(),
237245
/*ByVal=*/false);
246+
}
238247

239248
// Treat an enum type as its underlying type.
240249
if (const EnumType *EnumTy = Ty->getAs<EnumType>())
241250
Ty = EnumTy->getDecl()->getIntegerType();
242251

243252
// Integer types smaller than a register are extended.
244-
if (Size < 64 && Ty->isIntegerType())
253+
if (Size < 64 && Ty->isIntegerType()) {
254+
RegOffset += 1;
245255
return ABIArgInfo::getExtend(Ty);
256+
}
246257

247258
if (const auto *EIT = Ty->getAs<BitIntType>())
248-
if (EIT->getNumBits() < 64)
259+
if (EIT->getNumBits() < 64) {
260+
RegOffset += 1;
249261
return ABIArgInfo::getExtend(Ty);
262+
}
250263

251264
// Other non-aggregates go in registers.
252-
if (!isAggregateTypeForABI(Ty))
265+
if (!isAggregateTypeForABI(Ty)) {
266+
RegOffset += Size / 64;
253267
return ABIArgInfo::getDirect();
268+
}
254269

255270
// If a C++ object has either a non-trivial copy constructor or a non-trivial
256271
// destructor, it is passed with an explicit indirect pointer / sret pointer.
257-
if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI()))
272+
if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI())) {
273+
RegOffset += 1;
258274
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
259275
RAA == CGCXXABI::RAA_DirectInMemory);
276+
}
260277

261278
// This is a small aggregate type that should be passed in registers.
262279
// Build a coercion type from the LLVM struct type.
263280
llvm::StructType *StrTy = dyn_cast<llvm::StructType>(CGT.ConvertType(Ty));
264-
if (!StrTy)
281+
if (!StrTy) {
282+
RegOffset += Size / 64;
265283
return ABIArgInfo::getDirect();
284+
}
266285

267-
CoerceBuilder CB(getVMContext(), getDataLayout());
286+
CoerceBuilder CB(VMContext, getDataLayout());
268287
CB.addStruct(0, StrTy);
269288
// All structs, even empty ones, should take up a register argument slot,
270289
// so pin the minimum struct size to one bit.
271290
CB.pad(llvm::alignTo(
272291
std::max(CB.DL.getTypeSizeInBits(StrTy).getKnownMinValue(), uint64_t(1)),
273292
64));
293+
RegOffset += CB.Size / 64;
294+
295+
// If we're dealing with overaligned structs we may need to add a padding in
296+
// the front, to preserve the correct register-memory mapping.
297+
//
298+
// See SCD 2.4.1, pages 3P-11 and 3P-12.
299+
llvm::Type *Padding =
300+
NeedPadding ? llvm::Type::getInt64Ty(VMContext) : nullptr;
301+
RegOffset += NeedPadding ? 1 : 0;
274302

275303
// Try to use the original type for coercion.
276304
llvm::Type *CoerceTy = CB.isUsableType(StrTy) ? StrTy : CB.getType();
277305

278-
if (CB.InReg)
279-
return ABIArgInfo::getDirectInReg(CoerceTy);
280-
else
281-
return ABIArgInfo::getDirect(CoerceTy);
306+
ABIArgInfo AAI = ABIArgInfo::getDirect(CoerceTy, 0, Padding);
307+
AAI.setInReg(CB.InReg);
308+
return AAI;
282309
}
283310

284311
RValue SparcV9ABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
285312
QualType Ty, AggValueSlot Slot) const {
286-
ABIArgInfo AI = classifyType(Ty, 16 * 8);
287-
llvm::Type *ArgTy = CGT.ConvertType(Ty);
288-
if (AI.canHaveCoerceToType() && !AI.getCoerceToType())
289-
AI.setCoerceToType(ArgTy);
290-
291313
CharUnits SlotSize = CharUnits::fromQuantity(8);
314+
auto TInfo = getContext().getTypeInfoInChars(Ty);
292315

293-
CGBuilderTy &Builder = CGF.Builder;
294-
Address Addr = Address(Builder.CreateLoad(VAListAddr, "ap.cur"),
295-
getVAListElementType(CGF), SlotSize);
296-
llvm::Type *ArgPtrTy = CGF.UnqualPtrTy;
297-
298-
auto TypeInfo = getContext().getTypeInfoInChars(Ty);
299-
300-
Address ArgAddr = Address::invalid();
301-
CharUnits Stride;
302-
switch (AI.getKind()) {
303-
case ABIArgInfo::Expand:
304-
case ABIArgInfo::CoerceAndExpand:
305-
case ABIArgInfo::InAlloca:
306-
llvm_unreachable("Unsupported ABI kind for va_arg");
307-
308-
case ABIArgInfo::Extend: {
309-
Stride = SlotSize;
310-
CharUnits Offset = SlotSize - TypeInfo.Width;
311-
ArgAddr = Builder.CreateConstInBoundsByteGEP(Addr, Offset, "extend");
312-
break;
313-
}
314-
315-
case ABIArgInfo::Direct: {
316-
auto AllocSize = getDataLayout().getTypeAllocSize(AI.getCoerceToType());
317-
Stride = CharUnits::fromQuantity(AllocSize).alignTo(SlotSize);
318-
ArgAddr = Addr;
319-
break;
320-
}
321-
322-
case ABIArgInfo::Indirect:
323-
case ABIArgInfo::IndirectAliased:
324-
Stride = SlotSize;
325-
ArgAddr = Addr.withElementType(ArgPtrTy);
326-
ArgAddr = Address(Builder.CreateLoad(ArgAddr, "indirect.arg"), ArgTy,
327-
TypeInfo.Align);
328-
break;
316+
// Zero-sized types have a width of one byte for parameter passing purposes.
317+
TInfo.Width = std::max(TInfo.Width, CharUnits::fromQuantity(1));
329318

330-
case ABIArgInfo::Ignore:
331-
return Slot.asRValue();
332-
}
333-
334-
// Update VAList.
335-
Address NextPtr = Builder.CreateConstInBoundsByteGEP(Addr, Stride, "ap.next");
336-
Builder.CreateStore(NextPtr.emitRawPointer(CGF), VAListAddr);
337-
338-
return CGF.EmitLoadOfAnyValue(
339-
CGF.MakeAddrLValue(ArgAddr.withElementType(ArgTy), Ty), Slot);
319+
// Arguments bigger than 2*SlotSize bytes are passed indirectly.
320+
return emitVoidPtrVAArg(CGF, VAListAddr, Ty,
321+
/*IsIndirect=*/TInfo.Width > 2 * SlotSize, TInfo,
322+
SlotSize,
323+
/*AllowHigherAlign=*/true, Slot);
340324
}
341325

342326
void SparcV9ABIInfo::computeInfo(CGFunctionInfo &FI) const {
343-
FI.getReturnInfo() = classifyType(FI.getReturnType(), 32 * 8);
327+
unsigned RetOffset = 0;
328+
ABIArgInfo RetType = classifyType(FI.getReturnType(), 32 * 8, RetOffset);
329+
FI.getReturnInfo() = RetType;
330+
331+
// Indirect returns will have its pointer passed as an argument.
332+
unsigned ArgOffset = RetType.isIndirect() ? RetOffset : 0;
344333
for (auto &I : FI.arguments())
345-
I.info = classifyType(I.type, 16 * 8);
334+
I.info = classifyType(I.type, 16 * 8, ArgOffset);
346335
}
347336

348337
namespace {

clang/test/CodeGen/sparcv9-abi.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,35 @@ long double f_ld(long double x) { return x; }
2525
struct empty {};
2626
struct emptyarr { struct empty a[10]; };
2727

28+
// In 16-byte structs, 16-byte aligned members are expanded
29+
// to their corresponding i128/f128 types.
30+
struct align16_int { _Alignas(16) int x; };
31+
struct align16_mixed { _Alignas(16) int x; double y; };
32+
struct align16_longdouble { long double x; };
33+
2834
// CHECK-LABEL: define{{.*}} i64 @f_empty(i64 %x.coerce)
2935
struct empty f_empty(struct empty x) { return x; }
3036

3137
// CHECK-LABEL: define{{.*}} i64 @f_emptyarr(i64 %x.coerce)
3238
struct empty f_emptyarr(struct emptyarr x) { return x.a[0]; }
3339

40+
// CHECK-LABEL: define{{.*}} void @f_aligncaller(i64 %a.coerce0, i64 %a.coerce1)
41+
// CHECK-LABEL: declare{{.*}} void @f_aligncallee(i32 noundef signext, i64, i64, i64)
42+
void f_aligncallee(int pad, struct align16_int a);
43+
void f_aligncaller(struct align16_int a) {
44+
f_aligncallee(0, a);
45+
}
46+
47+
// CHECK-LABEL: define{{.*}} double @f_mixed_aligned(i64 noundef %a, i64 %0, i64 %b.coerce0, double %b.coerce1)
48+
double f_mixed_aligned(long a, struct align16_mixed b) {
49+
return b.y;
50+
}
51+
52+
// CHECK-LABEL: define{{.*}} fp128 @f_longdouble(i64 noundef %a, i64 %0, fp128 %b.coerce)
53+
long double f_longdouble(long a, struct align16_longdouble b) {
54+
return b.x;
55+
}
56+
3457
// CHECK-LABEL: define{{.*}} i64 @f_emptyvar(i32 noundef zeroext %count, ...)
3558
long f_emptyvar(unsigned count, ...) {
3659
long ret;
@@ -80,13 +103,25 @@ struct medium {
80103
int *c, *d;
81104
};
82105

106+
struct medium_aligned {
107+
_Alignas(16) int *a;
108+
int *b, *c, *d;
109+
};
110+
83111
// CHECK-LABEL: define{{.*}} %struct.medium @f_medium(ptr noundef %x)
84112
struct medium f_medium(struct medium x) {
85113
x.a += *x.b;
86114
x.b = 0;
87115
return x;
88116
}
89117

118+
// CHECK-LABEL: define{{.*}} %struct.medium_aligned @f_medium_aligned(ptr noundef %x)
119+
struct medium_aligned f_medium_aligned(struct medium_aligned x) {
120+
x.a += *x.b;
121+
x.b = 0;
122+
return x;
123+
}
124+
90125
// Large structs are also returned indirectly.
91126
struct large {
92127
int *a, *b;
@@ -101,6 +136,15 @@ struct large f_large(struct large x) {
101136
return x;
102137
}
103138

139+
// Large returns are converted into a pointer argument.
140+
// Such conversion should preserve the alignment of overaligned arguments.
141+
// define{{.*}} void @f_largereturn_aligned(ptr dead_on_unwind noalias writable sret(%struct.large) align 8 %agg.result, i64 %0, i64 %x.coerce0, i64 %x.coerce1)
142+
struct large f_largereturn_aligned(struct align16_int x) {
143+
struct large ret;
144+
ret.x = x.x;
145+
return ret;
146+
}
147+
104148
// A 64-bit struct fits in a register.
105149
struct reg {
106150
int a, b;
@@ -215,6 +259,18 @@ int f_variable(char *f, ...) {
215259
case 'm':
216260
s += *va_arg(ap, struct medium).a;
217261
break;
262+
263+
// CHECK: %[[CUR:[^ ]+]] = load ptr, ptr %ap
264+
// CHECK-DAG: %[[TMP:[^ ]+]] = getelementptr inbounds i8, ptr %[[CUR]], i32 15
265+
// CHECK-DAG: %[[ALIGNED:[^ ]+]] = call ptr @llvm.ptrmask.p0.i64(ptr %[[TMP]], i64 -16)
266+
// CHECK-DAG: %[[NXT:[^ ]+]] = getelementptr inbounds i8, ptr %[[ALIGNED]], i64 16
267+
// CHECK-DAG: store ptr %[[NXT]], ptr %ap
268+
// CHECK-DAG: call void @llvm.memcpy.p0.p0.i64(ptr align 16 {{.*}}, ptr align 16 %[[ALIGNED]], i64 16, i1 false)
269+
// CHECK: br
270+
case 'a':
271+
s += va_arg(ap, struct align16_int).x;
272+
break;
218273
}
274+
va_end(ap);
219275
return s;
220276
}

llvm/lib/Target/Sparc/SparcISelLowering.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ static bool Analyze_CC_Sparc64_Full(bool IsReturn, unsigned &ValNo, MVT &ValVT,
115115

116116
// Stack space is allocated for all arguments starting from [%fp+BIAS+128].
117117
unsigned size = (LocVT == MVT::f128) ? 16 : 8;
118-
Align alignment = (LocVT == MVT::f128) ? Align(16) : Align(8);
118+
Align alignment =
119+
(LocVT == MVT::f128 || ArgFlags.isSplit()) ? Align(16) : Align(8);
119120
unsigned Offset = State.AllocateStack(size, alignment);
120121
unsigned Reg = 0;
121122

llvm/test/CodeGen/SPARC/64abi.ll

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,15 +473,28 @@ declare i64 @receive_fp128(i64 %a, ...)
473473
; HARD-DAG: ldx [%sp+[[Offset0]]], %o2
474474
; HARD-DAG: ldx [%sp+[[Offset1]]], %o3
475475
; SOFT-DAG: mov %i0, %o0
476-
; SOFT-DAG: mov %i1, %o1
477476
; SOFT-DAG: mov %i2, %o2
477+
; SOFT-DAG: mov %i3, %o3
478478
; CHECK: call receive_fp128
479479
define i64 @test_fp128_variable_args(i64 %a, fp128 %b) {
480480
entry:
481481
%0 = call i64 (i64, ...) @receive_fp128(i64 %a, fp128 %b)
482482
ret i64 %0
483483
}
484484

485+
declare i64 @receive_i128(i64 %a, i128 %b)
486+
487+
; CHECK-LABEL: test_i128_args:
488+
; CHECK: mov %i3, %o3
489+
; CHECK: mov %i2, %o2
490+
; CHECK: mov %i0, %o0
491+
; CHECK: call receive_i128
492+
define i64 @test_i128_args(i64 %a, i128 %b) {
493+
entry:
494+
%0 = call i64 @receive_i128(i64 %a, i128 %b)
495+
ret i64 %0
496+
}
497+
485498
; CHECK-LABEL: test_call_libfunc:
486499
; HARD: st %f1, [%fp+[[Offset0:[0-9]+]]]
487500
; HARD: fmovs %f3, %f1

0 commit comments

Comments
 (0)