From: Reid Kleckner Date: Thu, 1 May 2014 03:07:18 +0000 (+0000) Subject: MS ABI x64: Don't destroy arguments twice on x64 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=380c0cfb644a51fb7bdecea32d83da5111dde0ab;p=clang MS ABI x64: Don't destroy arguments twice on x64 We were destroying them in the callee, and then again in the caller. We should use an EH-only cleanup and disable it at the point of the call for win64, even though we don't use inalloca. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@207733 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index e82b6defa6..4428b96316 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -2286,16 +2286,23 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E, // In the Microsoft C++ ABI, aggregate arguments are destructed by the callee. // However, we still have to push an EH-only cleanup in case we unwind before // we make it to the call. - if (HasAggregateEvalKind && args.isUsingInAlloca()) { - assert(getTarget().getTriple().getArch() == llvm::Triple::x86); - AggValueSlot Slot = createPlaceholderSlot(*this, type); + 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. + AggValueSlot Slot; + if (args.isUsingInAlloca()) + Slot = createPlaceholderSlot(*this, type); + else + Slot = CreateAggTemp(type, "agg.tmp"); Slot.setExternallyDestructed(); EmitAggExpr(E, Slot); RValue RV = Slot.asRValue(); args.add(RV, type); const CXXRecordDecl *RD = type->getAsCXXRecordDecl(); - if (RD->hasNonTrivialDestructor()) { + if (RD && RD->hasNonTrivialDestructor()) { // 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/test/CodeGenCXX/microsoft-abi-arg-order.cpp b/test/CodeGenCXX/microsoft-abi-arg-order.cpp index 01f6f47667..d26a515c85 100644 --- a/test/CodeGenCXX/microsoft-abi-arg-order.cpp +++ b/test/CodeGenCXX/microsoft-abi-arg-order.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -mconstructor-aliases -std=c++11 -fexceptions -emit-llvm %s -o - -triple=i386-pc-win32 | FileCheck %s +// RUN: %clang_cc1 -mconstructor-aliases -std=c++11 -fexceptions -emit-llvm %s -o - -triple=i386-pc-win32 | FileCheck %s -check-prefix=X86 +// RUN: %clang_cc1 -mconstructor-aliases -std=c++11 -fexceptions -emit-llvm %s -o - -triple=x86_64-pc-win32 | FileCheck %s -check-prefix=X64 struct A { A(int a); @@ -11,15 +12,22 @@ void foo(A a, A b, A c) { // Order of destruction should be left to right. // -// CHECK-LABEL: define void @"\01?foo@@YAXUA@@00@Z" -// CHECK: ([[argmem_ty:<{ %struct.A, %struct.A, %struct.A }>]]* inalloca) -// CHECK: %[[a:[^ ]*]] = getelementptr inbounds [[argmem_ty]]* %0, i32 0, i32 0 -// CHECK: %[[b:[^ ]*]] = getelementptr inbounds [[argmem_ty]]* %0, i32 0, i32 1 -// CHECK: %[[c:[^ ]*]] = getelementptr inbounds [[argmem_ty]]* %0, i32 0, i32 2 -// CHECK: call x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A* %[[a]]) -// CHECK: call x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A* %[[b]]) -// CHECK: call x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A* %[[c]]) -// CHECK: ret void +// X86-LABEL: define void @"\01?foo@@YAXUA@@00@Z" +// X86: ([[argmem_ty:<{ %struct.A, %struct.A, %struct.A }>]]* inalloca) +// X86: %[[a:[^ ]*]] = getelementptr inbounds [[argmem_ty]]* %0, i32 0, i32 0 +// X86: %[[b:[^ ]*]] = getelementptr inbounds [[argmem_ty]]* %0, i32 0, i32 1 +// X86: %[[c:[^ ]*]] = getelementptr inbounds [[argmem_ty]]* %0, i32 0, i32 2 +// X86: call x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A* %[[a]]) +// X86: call x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A* %[[b]]) +// X86: call x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A* %[[c]]) +// X86: ret void + +// X64-LABEL: define void @"\01?foo@@YAXUA@@00@Z" +// X64: (%struct.A* %[[a:[^,]*]], %struct.A* %[[b:[^,]*]], %struct.A* %[[c:[^)]*]]) +// X64: call void @"\01??1A@@QEAA@XZ"(%struct.A* %[[a]]) +// X64: call void @"\01??1A@@QEAA@XZ"(%struct.A* %[[b]]) +// X64: call void @"\01??1A@@QEAA@XZ"(%struct.A* %[[c]]) +// X64: ret void void call_foo() { @@ -29,22 +37,37 @@ void call_foo() { // Order of evaluation should be right to left, and we should clean up the right // things as we unwind. // -// CHECK-LABEL: define void @"\01?call_foo@@YAXXZ"() -// CHECK: call i8* @llvm.stacksave() -// CHECK: %[[argmem:[^ ]*]] = alloca inalloca [[argmem_ty]] -// CHECK: %[[arg3:[^ ]*]] = getelementptr inbounds [[argmem_ty]]* %[[argmem]], i32 0, i32 2 -// CHECK: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@H@Z"(%struct.A* %[[arg3]], i32 3) -// CHECK: %[[arg2:[^ ]*]] = getelementptr inbounds [[argmem_ty]]* %[[argmem]], i32 0, i32 1 -// CHECK: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@H@Z"(%struct.A* %[[arg2]], i32 2) -// CHECK: %[[arg1:[^ ]*]] = getelementptr inbounds [[argmem_ty]]* %[[argmem]], i32 0, i32 0 -// CHECK: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@H@Z"(%struct.A* %[[arg1]], i32 1) -// CHECK: invoke void @"\01?foo@@YAXUA@@00@Z"([[argmem_ty]]* inalloca %[[argmem]]) -// CHECK: call void @llvm.stackrestore -// CHECK: ret void +// X86-LABEL: define void @"\01?call_foo@@YAXXZ"() +// X86: call i8* @llvm.stacksave() +// X86: %[[argmem:[^ ]*]] = alloca inalloca [[argmem_ty]] +// X86: %[[arg3:[^ ]*]] = getelementptr inbounds [[argmem_ty]]* %[[argmem]], i32 0, i32 2 +// X86: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@H@Z"(%struct.A* %[[arg3]], i32 3) +// X86: %[[arg2:[^ ]*]] = getelementptr inbounds [[argmem_ty]]* %[[argmem]], i32 0, i32 1 +// X86: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@H@Z"(%struct.A* %[[arg2]], i32 2) +// X86: %[[arg1:[^ ]*]] = getelementptr inbounds [[argmem_ty]]* %[[argmem]], i32 0, i32 0 +// X86: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@H@Z"(%struct.A* %[[arg1]], i32 1) +// X86: invoke void @"\01?foo@@YAXUA@@00@Z"([[argmem_ty]]* inalloca %[[argmem]]) +// X86: call void @llvm.stackrestore +// X86: ret void +// +// lpad2: +// X86: call x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A* %[[arg2]]) +// X86: br label +// +// ehcleanup: +// X86: call x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A* %[[arg3]]) + +// X64-LABEL: define void @"\01?call_foo@@YAXXZ"() +// X64: call %struct.A* @"\01??0A@@QEAA@H@Z"(%struct.A* %[[arg3:[^,]*]], i32 3) +// X64: invoke %struct.A* @"\01??0A@@QEAA@H@Z"(%struct.A* %[[arg2:[^,]*]], i32 2) +// X64: invoke %struct.A* @"\01??0A@@QEAA@H@Z"(%struct.A* %[[arg1:[^,]*]], i32 1) +// X64: call void @"\01?foo@@YAXUA@@00@Z" +// X64: (%struct.A* %[[arg1]], %struct.A* %[[arg2]], %struct.A* %[[arg3]]) +// X64: ret void // // lpad2: -// CHECK: call x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A* %[[arg2]]) -// CHECK: br label +// X64: call void @"\01??1A@@QEAA@XZ"(%struct.A* %[[arg2]]) +// X64: br label // // ehcleanup: -// CHECK: call x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A* %[[arg3]]) +// X64: call void @"\01??1A@@QEAA@XZ"(%struct.A* %[[arg3]])