CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
146 lines
7.1 KiB
C++
146 lines
7.1 KiB
C++
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -mconstructor-aliases -emit-cir %s -o %t.cir
|
|
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
|
|
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -mconstructor-aliases -emit-llvm %s -o %t-cir.ll
|
|
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
|
|
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -mconstructor-aliases -emit-llvm %s -o %t.ll
|
|
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
|
|
|
|
// Minimal in-source declarations of the standard-library bits needed for a
|
|
// destroying operator delete so this test does not depend on a system header.
|
|
namespace std {
|
|
struct destroying_delete_t {
|
|
explicit destroying_delete_t() = default;
|
|
};
|
|
inline constexpr destroying_delete_t destroying_delete{};
|
|
} // namespace std
|
|
|
|
// A destroying operator delete: its second parameter is
|
|
// std::destroying_delete_t. Such an operator delete takes full responsibility
|
|
// for both destruction and deallocation of the object, so the 'delete'
|
|
// expression must not emit a separate destructor call or cleanup.
|
|
struct S {
|
|
void operator delete(S *, std::destroying_delete_t);
|
|
~S();
|
|
};
|
|
|
|
void test_destroying_delete(S *s) {
|
|
delete s;
|
|
}
|
|
|
|
// S::operator delete(S *, std::destroying_delete_t)
|
|
// CIR: cir.func private @_ZN1SdlEPS_St19destroying_delete_t(!cir.ptr<!rec_S> {llvm.noundef}, !rec_std3A3Adestroying_delete_t)
|
|
// LLVM: declare void @_ZN1SdlEPS_St19destroying_delete_t(ptr noundef, %"struct.std::destroying_delete_t")
|
|
|
|
// The destroying operator delete takes over the entire delete operation:
|
|
// no destructor call and no delete-cleanup are emitted in the caller; the
|
|
// operator delete is invoked inside the standard null-check if-region and is
|
|
// responsible for destroying the object itself.
|
|
|
|
// CIR: cir.func {{.*}} @_Z22test_destroying_deleteP1S(%[[ARG:.*]]: !cir.ptr<!rec_S> {{.*}})
|
|
// CIR: %[[S_ADDR:.*]] = cir.alloca !cir.ptr<!rec_S>, {{.*}} ["s", init]
|
|
// CIR: %[[TAG_ADDR:.*]] = cir.alloca !rec_std3A3Adestroying_delete_t, {{.*}} ["destroying.delete.tag"]
|
|
// CIR: cir.store %[[ARG]], %[[S_ADDR]]
|
|
// CIR: %[[S:.*]] = cir.load{{.*}} %[[S_ADDR]]
|
|
// CIR: %[[NULL:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_S>
|
|
// CIR: %[[NOT_NULL:.*]] = cir.cmp ne %[[S]], %[[NULL]] : !cir.ptr<!rec_S>
|
|
// CIR: cir.if %[[NOT_NULL]] {
|
|
// CIR: %[[TAG:.*]] = cir.load{{.*}} %[[TAG_ADDR]]
|
|
// CIR: cir.call @_ZN1SdlEPS_St19destroying_delete_t(%[[S]], %[[TAG]]){{.*}} : (!cir.ptr<!rec_S> {{.*}}, !rec_std3A3Adestroying_delete_t) -> ()
|
|
// CIR-NOT: cir.call @_ZN1SD{{[12]}}Ev
|
|
// CIR: }
|
|
// CIR: cir.return
|
|
|
|
// LLVM: define {{.*}} void @_Z22test_destroying_deleteP1S(ptr {{.*}} %[[ARG:.*]])
|
|
// LLVM: %[[S_ADDR:.*]] = alloca ptr
|
|
// LLVM: %[[TAG_ADDR:.*]] = alloca %"struct.std::destroying_delete_t"
|
|
// LLVM: store ptr %[[ARG]], ptr %[[S_ADDR]]
|
|
// LLVM: %[[S:.*]] = load ptr, ptr %[[S_ADDR]]
|
|
// LLVM: %[[NOT_NULL:.*]] = icmp ne ptr %[[S]], null
|
|
// LLVM: br i1 %[[NOT_NULL]], label %[[NOTNULL:.*]], label %[[END:.*]]
|
|
// LLVM: [[NOTNULL]]:
|
|
// LLVM: %[[TAG:.*]] = load %"struct.std::destroying_delete_t", ptr %[[TAG_ADDR]]
|
|
// LLVM: call void @_ZN1SdlEPS_St19destroying_delete_t(ptr noundef %[[S]], %"struct.std::destroying_delete_t" %[[TAG]])
|
|
// LLVM-NOT: call void @_ZN1SD{{[12]}}Ev
|
|
// LLVM: [[END]]:
|
|
// LLVM: ret void
|
|
|
|
// Classic codegen elides empty-class parameters at the ABI level, so the
|
|
// destroying_delete_t tag disappears from both the declaration and the call.
|
|
// Either way, no destructor call is emitted from the caller.
|
|
// OGCG: define {{.*}} void @_Z22test_destroying_deleteP1S(ptr {{.*}} %[[ARG:.*]])
|
|
// OGCG: %[[S_ADDR:.*]] = alloca ptr
|
|
// OGCG: store ptr %[[ARG]], ptr %[[S_ADDR]]
|
|
// OGCG: %[[S:.*]] = load ptr, ptr %[[S_ADDR]]
|
|
// OGCG: %[[ISNULL:.*]] = icmp eq ptr %[[S]], null
|
|
// OGCG: br i1 %[[ISNULL]], label %[[END:.*]], label %[[NOTNULL:.*]]
|
|
// OGCG: [[NOTNULL]]:
|
|
// OGCG: call void @_ZN1SdlEPS_St19destroying_delete_t(ptr noundef %[[S]])
|
|
// OGCG-NOT: call void @_ZN1SD{{[12]}}Ev
|
|
// OGCG: [[END]]:
|
|
// OGCG: ret void
|
|
// OGCG: declare void @_ZN1SdlEPS_St19destroying_delete_t(ptr noundef)
|
|
|
|
// A class with a virtual destructor and a destroying operator delete.
|
|
// Per the Itanium C++ ABI, the call is dispatched through the vtable's
|
|
// deleting-destructor slot (entry index 1); that function is responsible
|
|
// for running the destructor chain and then invoking the class-level
|
|
// destroying operator delete. The caller therefore does not call the
|
|
// destructor or the operator delete directly.
|
|
struct V {
|
|
virtual ~V();
|
|
void operator delete(V *, std::destroying_delete_t);
|
|
};
|
|
|
|
void test_virtual_destroying_delete(V *v) {
|
|
delete v;
|
|
}
|
|
|
|
// CIR: cir.func {{.*}} @_Z30test_virtual_destroying_deleteP1V(%[[ARG:.*]]: !cir.ptr<!rec_V> {{.*}})
|
|
// CIR: %[[V_ADDR:.*]] = cir.alloca !cir.ptr<!rec_V>, {{.*}} ["v", init]
|
|
// CIR: cir.store %[[ARG]], %[[V_ADDR]]
|
|
// CIR: %[[V:.*]] = cir.load{{.*}} %[[V_ADDR]]
|
|
// CIR: %[[NULL:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_V>
|
|
// CIR: %[[NOT_NULL:.*]] = cir.cmp ne %[[V]], %[[NULL]] : !cir.ptr<!rec_V>
|
|
// CIR: cir.if %[[NOT_NULL]] {
|
|
// CIR: %[[VPTR_ADDR:.*]] = cir.vtable.get_vptr %[[V]] : !cir.ptr<!rec_V> -> !cir.ptr<!cir.vptr>
|
|
// CIR: %[[VPTR:.*]] = cir.load{{.*}} %[[VPTR_ADDR]] : !cir.ptr<!cir.vptr>, !cir.vptr
|
|
// CIR: %[[SLOT_ADDR:.*]] = cir.vtable.get_virtual_fn_addr %[[VPTR]][1]
|
|
// CIR: %[[SLOT:.*]] = cir.load{{.*}} %[[SLOT_ADDR]]
|
|
// CIR: cir.call %[[SLOT]](%[[V]]){{.*}} : (!cir.ptr<!cir.func<(!cir.ptr<!rec_V>)>>, !cir.ptr<!rec_V> {{.*}}) -> ()
|
|
// CIR-NOT: cir.call @_ZN1VdlEPS_St19destroying_delete_t
|
|
// CIR-NOT: cir.call @_ZN1VD{{[12]}}Ev
|
|
// CIR: }
|
|
// CIR: cir.return
|
|
|
|
// LLVM: define {{.*}} void @_Z30test_virtual_destroying_deleteP1V(ptr {{.*}} %[[ARG:.*]])
|
|
// LLVM: %[[V_ADDR:.*]] = alloca ptr
|
|
// LLVM: store ptr %[[ARG]], ptr %[[V_ADDR]]
|
|
// LLVM: %[[V:.*]] = load ptr, ptr %[[V_ADDR]]
|
|
// LLVM: %[[NOT_NULL:.*]] = icmp ne ptr %[[V]], null
|
|
// LLVM: br i1 %[[NOT_NULL]], label %[[NOTNULL:.*]], label %[[END:.*]]
|
|
// LLVM: [[NOTNULL]]:
|
|
// LLVM: %[[VTABLE:.*]] = load ptr, ptr %[[V]]
|
|
// LLVM: %[[VFN_PTR:.*]] = getelementptr inbounds ptr, ptr %[[VTABLE]], i32 1
|
|
// LLVM: %[[VFN:.*]] = load ptr, ptr %[[VFN_PTR]]
|
|
// LLVM: call void %[[VFN]](ptr {{.*}} %[[V]])
|
|
// LLVM-NOT: call void @_ZN1VdlEPS_St19destroying_delete_t
|
|
// LLVM-NOT: call void @_ZN1VD{{[12]}}Ev
|
|
// LLVM: [[END]]:
|
|
// LLVM: ret void
|
|
|
|
// OGCG: define {{.*}} void @_Z30test_virtual_destroying_deleteP1V(ptr {{.*}} %[[ARG:.*]])
|
|
// OGCG: %[[V_ADDR:.*]] = alloca ptr
|
|
// OGCG: store ptr %[[ARG]], ptr %[[V_ADDR]]
|
|
// OGCG: %[[V:.*]] = load ptr, ptr %[[V_ADDR]]
|
|
// OGCG: %[[ISNULL:.*]] = icmp eq ptr %[[V]], null
|
|
// OGCG: br i1 %[[ISNULL]], label %[[END:.*]], label %[[NOTNULL:.*]]
|
|
// OGCG: [[NOTNULL]]:
|
|
// OGCG: %[[VTABLE:.*]] = load ptr, ptr %[[V]]
|
|
// OGCG: %[[VFN_PTR:.*]] = getelementptr inbounds ptr, ptr %[[VTABLE]], i64 1
|
|
// OGCG: %[[VFN:.*]] = load ptr, ptr %[[VFN_PTR]]
|
|
// OGCG: call void %[[VFN]](ptr {{.*}} %[[V]])
|
|
// OGCG-NOT: call void @_ZN1VdlEPS_St19destroying_delete_t
|
|
// OGCG-NOT: call void @_ZN1VD{{[12]}}Ev
|
|
// OGCG: [[END]]:
|
|
// OGCG: ret void
|