diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4474,23 +4474,24 @@ } else if (I->hasLValue()) { auto LV = I->getKnownLValue(); auto AS = LV.getAddressSpace(); + bool isByValOrRef = + ArgInfo.isIndirectAliased() || ArgInfo.getIndirectByVal(); - if (!ArgInfo.getIndirectByVal() || + if (!isByValOrRef || (LV.getAlignment() < getContext().getTypeAlignInChars(I->Ty))) { NeedCopy = true; } if (!getLangOpts().OpenCL) { - if ((ArgInfo.getIndirectByVal() && - (AS != LangAS::Default && - AS != CGM.getASTAllocaAddressSpace()))) { + if ((isByValOrRef && (AS != LangAS::Default && + AS != CGM.getASTAllocaAddressSpace()))) { NeedCopy = true; } } // For OpenCL even if RV is located in default or alloca address space // we don't want to perform address space cast for it. - else if ((ArgInfo.getIndirectByVal() && - Addr.getType()->getAddressSpace() != IRFuncTy-> - getParamType(FirstIRArg)->getPointerAddressSpace())) { + else if ((isByValOrRef && Addr.getType()->getAddressSpace() != + IRFuncTy->getParamType(FirstIRArg) + ->getPointerAddressSpace())) { NeedCopy = true; } } diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -7511,11 +7511,18 @@ return DefaultABIInfo::classifyReturnType(RetTy); } - ABIArgInfo classifyArgumentType(QualType RetTy) const { - if (RetTy->isAnyComplexType()) + ABIArgInfo classifyArgumentType(QualType Ty) const { + if (Ty->isAnyComplexType()) return complexArgInfo(); - return DefaultABIInfo::classifyArgumentType(RetTy); + // According to Section 3.5 of MSP430 EABI, structs, classes and unions + // are passed by reference. The callee is responsible for leaving them + // intact. + if (Ty->isStructureOrClassType() || Ty->isUnionType()) + return ABIArgInfo::getIndirectAliased( + getContext().getTypeAlignInChars(Ty), /*AddrSpace=*/0); + + return DefaultABIInfo::classifyArgumentType(Ty); } // Just copy the original implementations because diff --git a/clang/test/CodeGen/msp430-struct-or-union-args.c b/clang/test/CodeGen/msp430-struct-or-union-args.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/msp430-struct-or-union-args.c @@ -0,0 +1,123 @@ +// REQUIRES: msp430-registered-target +// Optimized to check that some of memcpy intrinsic invocations are optimized out. +// RUN: %clang -target msp430 -fno-inline-functions -S -Os -o- %s | FileCheck --check-prefixes=ASM %s +// Do not use any optimization to not clutter the output with deduced LLVM IR attributes. +// RUN: %clang -target msp430 -fno-inline-functions -S -emit-llvm -o- %s | FileCheck --check-prefixes=IR %s + +#include +#include + +// According to Section 3.5 of MSP430 EABI, structures and unions are passed +// by reference, even if an equally-sized integer argument could be passed +// in registers. + +struct S { + uint16_t a; +}; +union U { + uint16_t a; +}; + +// IR: %struct.S = type { i16 } +// IR: %union.U = type { i16 } + +_Static_assert(sizeof(struct S) * CHAR_BIT == 16, "Unexpected size"); +_Static_assert(sizeof(union U) * CHAR_BIT == 16, "Unexpected size"); + +extern struct S s; +extern union U u; + +// Cannot know for sure whether they change the argument +extern void leaf_s(struct S *x); +extern void leaf_u(union U *); + +// Callee is responsible for leaving the byref argument intact +void middle_s(struct S x) { +// IR: define {{(dso_local )?}}void @middle_s(%struct.S* byref(%struct.S) align 2 [[S_ARG:%.+]]) +// IR: [[S_COPY:%.+]] = alloca %struct.S, align 2 +// IR: [[S_CASTED_COPY:%.+]] = bitcast %struct.S* [[S_COPY]] to i8* +// IR: [[S_CASTED_ARG:%.+]] = bitcast %struct.S* [[S_ARG]] to i8* +// IR: call void @llvm.memcpy.p0i8.p0i8.i16(i8* align 2 [[S_CASTED_COPY]], i8* align 2 [[S_CASTED_ARG]], i16 2, i1 false) +// IR: call void @leaf_s(%struct.S* [[S_COPY]]) +// IR: ret void + +// ASM: middle_s: +// ASM: sub #2, r1 +// ... here memcpy() occurs ... +// ASM: mov r1, r12 +// ASM-NEXT: call #leaf_s +// ASM-NEXT: add #2, r1 +// ASM-NEXT: ret + leaf_s(&x); +} +void middle_u(union U x) { +// IR: define {{(dso_local )?}}void @middle_u(%union.U* byref(%union.U) align 2 [[U_ARG:%.+]]) +// IR: [[U_COPY:%.+]] = alloca %union.U, align 2 +// IR: [[U_CASTED_COPY:%.+]] = bitcast %union.U* [[U_COPY]] to i8* +// IR: [[U_CASTED_ARG:%.+]] = bitcast %union.U* [[U_ARG]] to i8* +// IR: call void @llvm.memcpy.p0i8.p0i8.i16(i8* align 2 [[U_CASTED_COPY]], i8* align 2 [[U_CASTED_ARG]], i16 2, i1 false) +// IR: call void @leaf_u(%union.U* [[U_COPY]]) +// IR: ret void + +// ASM: middle_u: +// ASM: sub #2, r1 +// ... here memcpy() occurs ... +// ASM: mov r1, r12 +// ASM-NEXT: call #leaf_u +// ASM-NEXT: add #2, r1 +// ASM-NEXT: ret + leaf_u(&x); +} + +void caller_s(void) { +// IR: define {{(dso_local )?}}void @caller_s() +// IR: call void @middle_s(%struct.S* byref(%struct.S) align 2 @s) +// IR: ret void + +// ASM: caller_s: +// ASM: mov #s, r12 +// ASM-NEXT: call #middle_s +// ASM-NEXT: ret + middle_s(s); +} +void caller_u(void) { +// IR: define dso_local void @caller_u() +// IR: call void @middle_u(%union.U* byref(%union.U) align 2 @u) +// IR: ret void + +// ASM: caller_u: +// ASM: mov #u, r12 +// ASM-NEXT: call #middle_u +// ASM-NEXT: ret + middle_u(u); +} + +// No need to create a temporary copy of the struct/union-typed argument +// if it is just passed to other function as-is. +// TODO For now, it works when size of structure is more than 8 bytes, +// otherwise the memcpy intrinsic will be replaced by InstCombiner. + +struct LL { + long long a[2]; +}; + +extern struct LL ll; + +extern void leaf(struct LL x); + +void middle(struct LL x) { +// ASM: middle: +// No stack-allocated objects: +// ASM-NOT: r1 +// ASM: call #leaf +// ASM-NEXT: ret + leaf(x); +} + +void caller(void) { +// ASM: caller: +// ASM: mov #ll, r12 +// ASM-NEXT: call #middle +// ASM-NEXT: ret + middle(ll); +}