[CodeGen] Store the return value of the target function call to the

thunk's return value slot directly when the return type is an aggregate
instead of doing so via a temporary

This fixes PR45997 (https://bugs.llvm.org/show_bug.cgi?id=45997), which
is caused by a bug that has existed since we started passing and
returning C++ structs with ObjC strong pointer members (see
https://reviews.llvm.org/D44908) or structs annotated with trivial_abi
directly.

rdar://problem/63740936

Differential Revision: https://reviews.llvm.org/D82513
This commit is contained in:
Akira Hatanaka 2020-07-10 17:24:12 -07:00
parent 351f2b3c0a
commit e9bf0a710c
4 changed files with 57 additions and 1 deletions

View File

@ -156,6 +156,7 @@ void CGCXXABI::setCXXABIThisValue(CodeGenFunction &CGF, llvm::Value *ThisPtr) {
void CGCXXABI::EmitReturnFromThunk(CodeGenFunction &CGF,
RValue RV, QualType ResultType) {
assert(!hasAggregateEvaluationKind(ResultType) && "cannot handle aggregates");
CGF.EmitReturnOfRValue(RV, ResultType);
}

View File

@ -363,7 +363,8 @@ void CodeGenFunction::EmitCallAndReturnForThunk(llvm::FunctionCallee Callee,
: FPT->getReturnType();
ReturnValueSlot Slot;
if (!ResultType->isVoidType() &&
CurFnInfo->getReturnInfo().getKind() == ABIArgInfo::Indirect)
(CurFnInfo->getReturnInfo().getKind() == ABIArgInfo::Indirect ||
hasAggregateEvaluationKind(ResultType)))
Slot = ReturnValueSlot(ReturnValue, ResultType.isVolatileQualified(),
/*IsUnused=*/false, /*IsExternallyDestructed=*/true);

View File

@ -43,6 +43,31 @@ struct HasNonTrivial {
NonTrivial m;
};
struct B0 {
virtual Small m0();
};
struct B1 {
virtual Small m0();
};
struct D0 : B0, B1 {
Small m0() override;
};
// CHECK-LABEL: define i64 @_ZThn8_N2D02m0Ev(
// CHECK: %[[RETVAL:.*]] = alloca %[[STRUCT_SMALL]], align 8
// CHECK: %[[CALL:.*]] = tail call i64 @_ZN2D02m0Ev(
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], %[[STRUCT_SMALL]]* %[[RETVAL]], i32 0, i32 0
// CHECK: %[[COERCE_VAL_IP:.*]] = inttoptr i64 %[[CALL]] to i32*
// CHECK: store i32* %[[COERCE_VAL_IP]], i32** %[[COERCE_DIVE]], align 8
// CHECK: %[[COERCE_DIVE2:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], %[[STRUCT_SMALL]]* %[[RETVAL]], i32 0, i32 0
// CHECK: %[[V3:.*]] = load i32*, i32** %[[COERCE_DIVE2]], align 8
// CHECK: %[[COERCE_VAL_PI:.*]] = ptrtoint i32* %[[V3]] to i64
// CHECK: ret i64 %[[COERCE_VAL_PI]]
Small D0::m0() { return {}; }
// CHECK: define void @_Z14testParamSmall5Small(i64 %[[A_COERCE:.*]])
// CHECK: %[[A:.*]] = alloca %[[STRUCT_SMALL]], align 8
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], %[[STRUCT_SMALL]]* %[[A]], i32 0, i32 0

View File

@ -178,3 +178,32 @@ void testParamContainsNonTrivial(ContainsNonTrivial a) {
void testCallContainsNonTrivial(ContainsNonTrivial *a) {
testParamContainsNonTrivial(*a);
}
namespace testThunk {
// CHECK-LABEL: define i64 @_ZThn8_N9testThunk2D02m0Ev(
// CHECK: %[[RETVAL:.*]] = alloca %[[STRUCT_STRONG]], align 8
// CHECK: %[[CALL:.*]] = tail call i64 @_ZN9testThunk2D02m0Ev(
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_STRONG]], %[[STRUCT_STRONG]]* %[[RETVAL]], i32 0, i32 0
// CHECK: %[[COERCE_VAL_IP:.*]] = inttoptr i64 %[[CALL]] to i8*
// CHECK: store i8* %[[COERCE_VAL_IP]], i8** %[[COERCE_DIVE]], align 8
// CHECK: %[[COERCE_DIVE2:.*]] = getelementptr inbounds %[[STRUCT_STRONG]], %[[STRUCT_STRONG]]* %[[RETVAL]], i32 0, i32 0
// CHECK: %[[V3:.*]] = load i8*, i8** %[[COERCE_DIVE2]], align 8
// CHECK: %[[COERCE_VAL_PI:.*]] = ptrtoint i8* %[[V3]] to i64
// CHECK: ret i64 %[[COERCE_VAL_PI]]
struct B0 {
virtual Strong m0();
};
struct B1 {
virtual Strong m0();
};
struct D0 : B0, B1 {
Strong m0() override;
};
Strong D0::m0() { return {}; }
}