diff options
| -rw-r--r-- | clang/lib/CodeGen/CGCall.cpp | 15 | ||||
| -rw-r--r-- | clang/lib/CodeGen/MicrosoftCXXABI.cpp | 39 | ||||
| -rw-r--r-- | clang/test/CodeGenCXX/microsoft-abi-arg-order.cpp | 1 | ||||
| -rw-r--r-- | clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp | 36 |
4 files changed, 74 insertions, 17 deletions
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 4428b963162..da504739836 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2289,20 +2289,25 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E, if (HasAggregateEvalKind && CGM.getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee()) { // If we're using inalloca, use the argument memory. Otherwise, use a - // temporary. Either way, the aggregate is destroyed externally in the - // callee. + // temporary. AggValueSlot Slot; if (args.isUsingInAlloca()) Slot = createPlaceholderSlot(*this, type); else Slot = CreateAggTemp(type, "agg.tmp"); - Slot.setExternallyDestructed(); + + const CXXRecordDecl *RD = type->getAsCXXRecordDecl(); + bool DestroyedInCallee = + RD && RD->hasNonTrivialDestructor() && + CGM.getCXXABI().getRecordArgABI(RD) != CGCXXABI::RAA_Default; + if (DestroyedInCallee) + Slot.setExternallyDestructed(); + EmitAggExpr(E, Slot); RValue RV = Slot.asRValue(); args.add(RV, type); - const CXXRecordDecl *RD = type->getAsCXXRecordDecl(); - if (RD && RD->hasNonTrivialDestructor()) { + if (DestroyedInCallee) { // Create a no-op GEP between the placeholder and the cleanup so we can // RAUW it successfully. It also serves as a marker of the first // instruction where the cleanup is active. diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp index 869734ab4e4..f22b96a6a07 100644 --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -44,17 +44,7 @@ public: return !RD->isPOD(); } - RecordArgABI getRecordArgABI(const CXXRecordDecl *RD) const override { - if (RD->hasNonTrivialCopyConstructor() || RD->hasNonTrivialDestructor()) { - llvm::Triple::ArchType Arch = CGM.getTarget().getTriple().getArch(); - if (Arch == llvm::Triple::x86) - return RAA_DirectInMemory; - // On x64, pass non-trivial records indirectly. - // FIXME: Test other Windows architectures. - return RAA_Indirect; - } - return RAA_Default; - } + RecordArgABI getRecordArgABI(const CXXRecordDecl *RD) const override; StringRef GetPureVirtualCallName() override { return "_purecall"; } // No known support for deleted functions in MSVC yet, so this choice is @@ -407,6 +397,33 @@ private: } +CGCXXABI::RecordArgABI +MicrosoftCXXABI::getRecordArgABI(const CXXRecordDecl *RD) const { + switch (CGM.getTarget().getTriple().getArch()) { + default: + // FIXME: Implement for other architectures. + return RAA_Default; + + case llvm::Triple::x86: + // 32-bit x86 constructs non-trivial objects directly in outgoing argument + // slots. LLVM uses the inalloca attribute to implement this. + if (RD->hasNonTrivialCopyConstructor() || RD->hasNonTrivialDestructor()) + return RAA_DirectInMemory; + return RAA_Default; + + case llvm::Triple::x86_64: + // Win64 passes objects with non-trivial copy ctors indirectly. + if (RD->hasNonTrivialCopyConstructor()) + return RAA_Indirect; + // Win64 passes objects larger than 8 bytes indirectly. + if (getContext().getTypeSize(RD->getTypeForDecl()) > 64) + return RAA_Indirect; + return RAA_Default; + } + + llvm_unreachable("invalid enum"); +} + llvm::Value *MicrosoftCXXABI::adjustToCompleteObject(CodeGenFunction &CGF, llvm::Value *ptr, QualType type) { diff --git a/clang/test/CodeGenCXX/microsoft-abi-arg-order.cpp b/clang/test/CodeGenCXX/microsoft-abi-arg-order.cpp index d26a515c85e..b47508b302d 100644 --- a/clang/test/CodeGenCXX/microsoft-abi-arg-order.cpp +++ b/clang/test/CodeGenCXX/microsoft-abi-arg-order.cpp @@ -3,6 +3,7 @@ struct A { A(int a); + A(const A &o); ~A(); int a; }; diff --git a/clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp b/clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp index bc937491a57..1e333664c8b 100644 --- a/clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp +++ b/clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp @@ -47,6 +47,12 @@ struct Big { int a, b, c, d, e, f; }; +struct BigWithDtor { + BigWithDtor(); + ~BigWithDtor(); + int a, b, c, d, e, f; +}; + // WIN32: declare void @"{{.*take_bools_and_chars.*}}" // WIN32: (<{ i8, [3 x i8], i8, [3 x i8], %struct.SmallWithDtor, // WIN32: i8, [3 x i8], i8, [3 x i8], i32, i8, [3 x i8] }>* inalloca) @@ -114,15 +120,43 @@ void small_arg_with_dtor(SmallWithDtor s) {} // WIN32: define void @"\01?small_arg_with_dtor@@YAXUSmallWithDtor@@@Z"(<{ %struct.SmallWithDtor }>* inalloca) {{.*}} { // WIN32: call x86_thiscallcc void @"\01??1SmallWithDtor@@QAE@XZ" // WIN32: } -// WIN64: define void @"\01?small_arg_with_dtor@@YAXUSmallWithDtor@@@Z"(%struct.SmallWithDtor* %s) {{.*}} { +// WIN64: define void @"\01?small_arg_with_dtor@@YAXUSmallWithDtor@@@Z"(i32 %s.coerce) {{.*}} { // WIN64: call void @"\01??1SmallWithDtor@@QEAA@XZ" // WIN64: } +void call_small_arg_with_dtor() { + small_arg_with_dtor(SmallWithDtor()); +} +// The temporary is copied, so it's destroyed in the caller as well as the +// callee. +// WIN64-LABEL: define void @"\01?call_small_arg_with_dtor@@YAXXZ"() +// WIN64: call %struct.SmallWithDtor* @"\01??0SmallWithDtor@@QEAA@XZ" +// WIN64: call void @"\01?small_arg_with_dtor@@YAXUSmallWithDtor@@@Z"(i32 %{{.*}}) +// WIN64: call void @"\01??1SmallWithDtor@@QEAA@XZ" +// WIN64: ret void + // Test that references aren't destroyed in the callee. void ref_small_arg_with_dtor(const SmallWithDtor &s) { } // WIN32: define void @"\01?ref_small_arg_with_dtor@@YAXABUSmallWithDtor@@@Z"(%struct.SmallWithDtor* %s) {{.*}} { // WIN32-NOT: call x86_thiscallcc void @"\01??1SmallWithDtor@@QAE@XZ" // WIN32: } +// WIN64-LABEL: define void @"\01?ref_small_arg_with_dtor@@YAXAEBUSmallWithDtor@@@Z"(%struct.SmallWithDtor* %s) + +void big_arg_with_dtor(BigWithDtor s) {} +// WIN64-LABEL: define void @"\01?big_arg_with_dtor@@YAXUBigWithDtor@@@Z"(%struct.BigWithDtor* %s) +// WIN64: call void @"\01??1BigWithDtor@@QEAA@XZ" +// WIN64: } + +void call_big_arg_with_dtor() { + big_arg_with_dtor(BigWithDtor()); +} +// We can elide the copy of the temporary in the caller, because this object is +// larger than 8 bytes and is passed indirectly. +// WIN64-LABEL: define void @"\01?call_big_arg_with_dtor@@YAXXZ"() +// WIN64: call %struct.BigWithDtor* @"\01??0BigWithDtor@@QEAA@XZ" +// WIN64: call void @"\01?big_arg_with_dtor@@YAXUBigWithDtor@@@Z"(%struct.BigWithDtor* %{{.*}}) +// WIN64-NOT: call void @"\01??1BigWithDtor@@QEAA@XZ" +// WIN64: ret void // Test that temporaries passed by reference are destroyed in the caller. void temporary_ref_with_dtor() { |

