Files
llvm-project/clang/test/CodeGenCXX/uncopyable-args.cpp
Aiden Grossman e2d7cd685d [IR] Make dead_on_return attribute optionally sized
This patch makes the dead_on_return parameter attribute optionally require a number
of bytes to be passed in to specify the number of bytes known to be dead
upon function return/unwind. This is aimed at enabling annotating the
this pointer in C++ destructors with dead_on_return in clang. We need
this to handle cases like the following:

```
struct X {
  int n;
  ~X() {
    this[n].n = 0;
  }
};
void f() {
  X xs[] = {42, -1};
}
```

Where we only certain that sizeof(X) bytes are dead upon return of ~X.
Otherwise DSE would be able to eliminate the store in ~X which would not
be correct.

This patch only does the wiring within IR. Future patches will make
clang emit correct sizing information and update DSE to only delete
stores to objects marked dead_on_return that are provably in bounds of
the number of bytes specified to be dead_on_return.

Reviewers: nikic, alinas, antoniofrighetto

Pull Request: https://github.com/llvm/llvm-project/pull/171712
2026-01-21 08:22:05 -08:00

372 lines
11 KiB
C++

// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=NEWABI
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown -fclang-abi-compat=4.0 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=OLDABI
// RUN: %clang_cc1 -std=c++11 -triple x86_64-scei-ps4 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=OLDABI
// RUN: %clang_cc1 -std=c++11 -triple x86_64-sie-ps5 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=NEWABI
// RUN: %clang_cc1 -std=c++11 -triple x86_64-windows-msvc -emit-llvm -o - %s -fms-compatibility -fms-compatibility-version=18 | FileCheck %s -check-prefix=WIN64 -check-prefix=WIN64-18
// RUN: %clang_cc1 -std=c++11 -triple x86_64-windows-msvc -emit-llvm -o - %s -fms-compatibility -fms-compatibility-version=19 | FileCheck %s -check-prefix=WIN64 -check-prefix=WIN64-19
namespace trivial {
// Trivial structs should be passed directly.
struct A {
void *p;
};
void foo(A);
void bar() {
foo({});
}
// CHECK-LABEL: define{{.*}} void @_ZN7trivial3barEv()
// CHECK: alloca %"struct.trivial::A"
// CHECK: load ptr, ptr
// CHECK: call void @_ZN7trivial3fooENS_1AE(ptr %{{.*}})
// CHECK-LABEL: declare void @_ZN7trivial3fooENS_1AE(ptr)
// WIN64-LABEL: declare dso_local void @"?foo@trivial@@YAXUA@1@@Z"(i64)
}
namespace default_ctor {
struct A {
A();
void *p;
};
void foo(A);
void bar() {
// Core issue 1590. We can pass this type in registers, even though C++
// normally doesn't permit copies when using braced initialization.
foo({});
}
// CHECK-LABEL: define{{.*}} void @_ZN12default_ctor3barEv()
// CHECK: alloca %"struct.default_ctor::A"
// CHECK: call void @_Z{{.*}}C1Ev(
// CHECK: load ptr, ptr
// CHECK: call void @_ZN12default_ctor3fooENS_1AE(ptr %{{.*}})
// CHECK-LABEL: declare void @_ZN12default_ctor3fooENS_1AE(ptr)
// WIN64-LABEL: declare dso_local void @"?foo@default_ctor@@YAXUA@1@@Z"(i64)
}
namespace move_ctor {
// The presence of a move constructor implicitly deletes the trivial copy ctor
// and means that we have to pass this struct by address.
struct A {
A();
A(A &&o);
void *p;
};
void foo(A);
void bar() {
foo({});
}
// CHECK-LABEL: define{{.*}} void @_ZN9move_ctor3barEv()
// CHECK: call void @_Z{{.*}}C1Ev(
// CHECK-NOT: call
// NEWABI: call void @_ZN9move_ctor3fooENS_1AE(ptr noundef dead_on_return %{{.*}})
// OLDABI: call void @_ZN9move_ctor3fooENS_1AE(ptr %{{.*}})
// NEWABI-LABEL: declare void @_ZN9move_ctor3fooENS_1AE(ptr noundef dead_on_return)
// OLDABI-LABEL: declare void @_ZN9move_ctor3fooENS_1AE(ptr)
// WIN64-LABEL: declare dso_local void @"?foo@move_ctor@@YAXUA@1@@Z"(ptr noundef dead_on_return)
}
namespace all_deleted {
struct A {
A();
A(const A &o) = delete;
A(A &&o) = delete;
void *p;
};
void foo(A);
void bar() {
foo({});
}
// CHECK-LABEL: define{{.*}} void @_ZN11all_deleted3barEv()
// CHECK: call void @_Z{{.*}}C1Ev(
// CHECK-NOT: call
// NEWABI: call void @_ZN11all_deleted3fooENS_1AE(ptr noundef dead_on_return %{{.*}})
// OLDABI: call void @_ZN11all_deleted3fooENS_1AE(ptr %{{.*}})
// NEWABI-LABEL: declare void @_ZN11all_deleted3fooENS_1AE(ptr noundef dead_on_return)
// OLDABI-LABEL: declare void @_ZN11all_deleted3fooENS_1AE(ptr)
// WIN64-LABEL: declare dso_local void @"?foo@all_deleted@@YAXUA@1@@Z"(ptr noundef dead_on_return)
}
namespace implicitly_deleted {
struct A {
A();
A &operator=(A &&o);
void *p;
};
void foo(A);
void bar() {
foo({});
}
// CHECK-LABEL: define{{.*}} void @_ZN18implicitly_deleted3barEv()
// CHECK: call void @_Z{{.*}}C1Ev(
// CHECK-NOT: call
// NEWABI: call void @_ZN18implicitly_deleted3fooENS_1AE(ptr noundef dead_on_return %{{.*}})
// OLDABI: call void @_ZN18implicitly_deleted3fooENS_1AE(ptr %{{.*}})
// NEWABI-LABEL: declare void @_ZN18implicitly_deleted3fooENS_1AE(ptr noundef dead_on_return)
// OLDABI-LABEL: declare void @_ZN18implicitly_deleted3fooENS_1AE(ptr)
// In MSVC 2013, the copy ctor is not deleted by a move assignment. In MSVC 2015, it is.
// WIN64-18-LABEL: declare dso_local void @"?foo@implicitly_deleted@@YAXUA@1@@Z"(i64
// WIN64-19-LABEL: declare dso_local void @"?foo@implicitly_deleted@@YAXUA@1@@Z"(ptr noundef dead_on_return)
}
namespace one_deleted {
struct A {
A();
A(A &&o) = delete;
void *p;
};
void foo(A);
void bar() {
foo({});
}
// CHECK-LABEL: define{{.*}} void @_ZN11one_deleted3barEv()
// CHECK: call void @_Z{{.*}}C1Ev(
// CHECK-NOT: call
// NEWABI: call void @_ZN11one_deleted3fooENS_1AE(ptr noundef dead_on_return %{{.*}})
// OLDABI: call void @_ZN11one_deleted3fooENS_1AE(ptr %{{.*}})
// NEWABI-LABEL: declare void @_ZN11one_deleted3fooENS_1AE(ptr noundef dead_on_return)
// OLDABI-LABEL: declare void @_ZN11one_deleted3fooENS_1AE(ptr)
// WIN64-LABEL: declare dso_local void @"?foo@one_deleted@@YAXUA@1@@Z"(ptr noundef dead_on_return)
}
namespace copy_defaulted {
struct A {
A();
A(const A &o) = default;
A(A &&o) = delete;
void *p;
};
void foo(A);
void bar() {
foo({});
}
// CHECK-LABEL: define{{.*}} void @_ZN14copy_defaulted3barEv()
// CHECK: call void @_Z{{.*}}C1Ev(
// CHECK: load ptr, ptr
// CHECK: call void @_ZN14copy_defaulted3fooENS_1AE(ptr %{{.*}})
// CHECK-LABEL: declare void @_ZN14copy_defaulted3fooENS_1AE(ptr)
// WIN64-LABEL: declare dso_local void @"?foo@copy_defaulted@@YAXUA@1@@Z"(i64)
}
namespace move_defaulted {
struct A {
A();
A(const A &o) = delete;
A(A &&o) = default;
void *p;
};
void foo(A);
void bar() {
foo({});
}
// CHECK-LABEL: define{{.*}} void @_ZN14move_defaulted3barEv()
// CHECK: call void @_Z{{.*}}C1Ev(
// CHECK: load ptr, ptr
// CHECK: call void @_ZN14move_defaulted3fooENS_1AE(ptr %{{.*}})
// CHECK-LABEL: declare void @_ZN14move_defaulted3fooENS_1AE(ptr)
// WIN64-LABEL: declare dso_local void @"?foo@move_defaulted@@YAXUA@1@@Z"(ptr noundef dead_on_return)
}
namespace trivial_defaulted {
struct A {
A();
A(const A &o) = default;
void *p;
};
void foo(A);
void bar() {
foo({});
}
// CHECK-LABEL: define{{.*}} void @_ZN17trivial_defaulted3barEv()
// CHECK: call void @_Z{{.*}}C1Ev(
// CHECK: load ptr, ptr
// CHECK: call void @_ZN17trivial_defaulted3fooENS_1AE(ptr %{{.*}})
// CHECK-LABEL: declare void @_ZN17trivial_defaulted3fooENS_1AE(ptr)
// WIN64-LABEL: declare dso_local void @"?foo@trivial_defaulted@@YAXUA@1@@Z"(i64)
}
namespace two_copy_ctors {
struct A {
A();
A(const A &) = default;
A(const A &, int = 0);
void *p;
};
struct B : A {};
void foo(B);
void bar() {
foo({});
}
// CHECK-LABEL: define{{.*}} void @_ZN14two_copy_ctors3barEv()
// CHECK: call void @_Z{{.*}}C1Ev(
// NEWABI: call void @_ZN14two_copy_ctors3fooENS_1BE(ptr noundef dead_on_return %{{.*}})
// OLDABI: call void @_ZN14two_copy_ctors3fooENS_1BE(ptr noundef byval
// NEWABI-LABEL: declare void @_ZN14two_copy_ctors3fooENS_1BE(ptr noundef dead_on_return)
// OLDABI-LABEL: declare void @_ZN14two_copy_ctors3fooENS_1BE(ptr noundef byval
// WIN64-LABEL: declare dso_local void @"?foo@two_copy_ctors@@YAXUB@1@@Z"(ptr noundef dead_on_return)
}
namespace definition_only {
struct A {
A();
A(A &&o);
void *p;
};
void *foo(A a) { return a.p; }
// NEWABI-LABEL: define{{.*}} ptr @_ZN15definition_only3fooENS_1AE(ptr
// OLDABI-LABEL: define{{.*}} ptr @_ZN15definition_only3fooENS_1AE(ptr
// WIN64-LABEL: define dso_local noundef ptr @"?foo@definition_only@@YAPEAXUA@1@@Z"(ptr
}
namespace deleted_by_member {
struct B {
B();
B(B &&o);
void *p;
};
struct A {
A();
B b;
};
void *foo(A a) { return a.b.p; }
// NEWABI-LABEL: define{{.*}} ptr @_ZN17deleted_by_member3fooENS_1AE(ptr
// OLDABI-LABEL: define{{.*}} ptr @_ZN17deleted_by_member3fooENS_1AE(ptr
// WIN64-LABEL: define dso_local noundef ptr @"?foo@deleted_by_member@@YAPEAXUA@1@@Z"(ptr
}
namespace deleted_by_base {
struct B {
B();
B(B &&o);
void *p;
};
struct A : B {
A();
};
void *foo(A a) { return a.p; }
// NEWABI-LABEL: define{{.*}} ptr @_ZN15deleted_by_base3fooENS_1AE(ptr
// OLDABI-LABEL: define{{.*}} ptr @_ZN15deleted_by_base3fooENS_1AE(ptr
// WIN64-LABEL: define dso_local noundef ptr @"?foo@deleted_by_base@@YAPEAXUA@1@@Z"(ptr
}
namespace deleted_by_member_copy {
struct B {
B();
B(const B &o) = delete;
void *p;
};
struct A {
A();
B b;
};
void *foo(A a) { return a.b.p; }
// NEWABI-LABEL: define{{.*}} ptr @_ZN22deleted_by_member_copy3fooENS_1AE(ptr
// OLDABI-LABEL: define{{.*}} ptr @_ZN22deleted_by_member_copy3fooENS_1AE(ptr
// WIN64-LABEL: define dso_local noundef ptr @"?foo@deleted_by_member_copy@@YAPEAXUA@1@@Z"(ptr
}
namespace deleted_by_base_copy {
struct B {
B();
B(const B &o) = delete;
void *p;
};
struct A : B {
A();
};
void *foo(A a) { return a.p; }
// NEWABI-LABEL: define{{.*}} ptr @_ZN20deleted_by_base_copy3fooENS_1AE(ptr
// OLDABI-LABEL: define{{.*}} ptr @_ZN20deleted_by_base_copy3fooENS_1AE(ptr
// WIN64-LABEL: define dso_local noundef ptr @"?foo@deleted_by_base_copy@@YAPEAXUA@1@@Z"(ptr
}
namespace explicit_delete {
struct A {
A();
A(const A &o) = delete;
void *p;
};
// NEWABI-LABEL: define{{.*}} ptr @_ZN15explicit_delete3fooENS_1AE(ptr
// OLDABI-LABEL: define{{.*}} ptr @_ZN15explicit_delete3fooENS_1AE(ptr
// WIN64-LABEL: define dso_local noundef ptr @"?foo@explicit_delete@@YAPEAXUA@1@@Z"(ptr
void *foo(A a) { return a.p; }
}
namespace implicitly_deleted_copy_ctor {
struct A {
// No move ctor due to copy assignment.
A &operator=(const A&);
// Deleted copy ctor due to rvalue ref member.
int &&ref;
};
// NEWABI-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_ctor3fooENS_1AE(ptr
// OLDABI-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_ctor3fooENS_1AE(ptr
// WIN64-LABEL: define {{.*}} @"?foo@implicitly_deleted_copy_ctor@@YAAEAHUA@1@@Z"(ptr
int &foo(A a) { return a.ref; }
struct B {
// Passed direct: has non-deleted trivial copy ctor.
B &operator=(const B&);
int &ref;
};
int &foo(B b) { return b.ref; }
// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_ctor3fooENS_1BE(ptr
// WIN64-LABEL: define {{.*}} @"?foo@implicitly_deleted_copy_ctor@@YAAEAHUB@1@@Z"(i64
struct X { X(const X&); };
struct Y { Y(const Y&) = default; };
union C {
C &operator=(const C&);
// Passed indirect: copy ctor deleted due to variant member with nontrivial copy ctor.
X x;
int n;
};
int foo(C c) { return c.n; }
// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_ctor3fooENS_1CE(ptr
// WIN64-LABEL: define {{.*}} @"?foo@implicitly_deleted_copy_ctor@@YAHTC@1@@Z"(ptr
struct D {
D &operator=(const D&);
// Passed indirect: copy ctor deleted due to variant member with nontrivial copy ctor.
union {
X x;
int n;
};
};
int foo(D d) { return d.n; }
// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_ctor3fooENS_1DE(ptr
// WIN64-LABEL: define {{.*}} @"?foo@implicitly_deleted_copy_ctor@@YAHUD@1@@Z"(ptr
union E {
// Passed direct: has non-deleted trivial copy ctor.
E &operator=(const E&);
Y y;
int n;
};
int foo(E e) { return e.n; }
// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_ctor3fooENS_1EE(i32
// WIN64-LABEL: define {{.*}} @"?foo@implicitly_deleted_copy_ctor@@YAHTE@1@@Z"(i32
struct F {
// Passed direct: has non-deleted trivial copy ctor.
F &operator=(const F&);
union {
Y y;
int n;
};
};
int foo(F f) { return f.n; }
// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_ctor3fooENS_1FE(i32
// WIN64-LABEL: define {{.*}} @"?foo@implicitly_deleted_copy_ctor@@YAHUF@1@@Z"(i32
}