summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/lib/CodeGen/CGCall.cpp15
-rw-r--r--clang/lib/CodeGen/MicrosoftCXXABI.cpp39
-rw-r--r--clang/test/CodeGenCXX/microsoft-abi-arg-order.cpp1
-rw-r--r--clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp36
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() {
OpenPOWER on IntegriCloud