Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2726,9 +2726,29 @@ mlir::Value CIRGenItaniumCXXABI::emitDynamicCast(CIRGenFunction &CGF,
// If the destination is effectively final, the cast succeeds if and only
// if the dynamic type of the pointer is exactly the destination type.
if (DestRecordTy->getAsCXXRecordDecl()->isEffectivelyFinal() &&
CGF.CGM.getCodeGenOpts().OptimizationLevel > 0)
CGF.CGM.getCodeGenOpts().OptimizationLevel > 0) {
CIRGenBuilderTy &builder = CGF.getBuilder();
// If this isn't a reference cast, check the pointer to see if it's null.
if (!isRefCast) {
mlir::Value srcPtrIsNull = builder.createPtrIsNull(Src.getPointer());
return cir::TernaryOp::create(
builder, Loc, srcPtrIsNull,
[&](mlir::OpBuilder, mlir::Location) {
builder.createYield(
Loc, builder.getNullPtr(DestCIRTy, Loc).getResult());
},
[&](mlir::OpBuilder &, mlir::Location) {
mlir::Value exactCast = emitExactDynamicCast(
*this, CGF, Loc, SrcRecordTy, DestRecordTy, DestCIRTy,
isRefCast, Src);
builder.createYield(Loc, exactCast);
})
.getResult();
}

return emitExactDynamicCast(*this, CGF, Loc, SrcRecordTy, DestRecordTy,
DestCIRTy, isRefCast, Src);
}

auto castInfo = emitDynamicCastInfo(CGF, Loc, SrcRecordTy, DestRecordTy);
return CGF.getBuilder().createDynCast(Loc, Src.getPointer(), DestCIRTy,
Expand Down
71 changes: 57 additions & 14 deletions clang/test/CIR/CodeGen/dynamic-cast-exact.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -O1 -fclangir -clangir-disable-passes -emit-cir -o %t.cir %s
// RUN: FileCheck --input-file=%t.cir %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -O1 -fclangir -emit-llvm -fno-clangir-call-conv-lowering -o %t.ll %s
// RUN: FileCheck --input-file=%t.ll --check-prefix=LLVM %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -O1 -fclangir -emit-llvm -fno-clangir-call-conv-lowering -o %t-cir.ll %s
// RUN: FileCheck --input-file=%t-cir.ll --check-prefix=LLVM %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -O1 -emit-llvm -o %t.ll %s
// RUN: FileCheck --input-file=%t.ll --check-prefix=OGCG %s

struct Base1 {
virtual ~Base1();
Expand All @@ -16,26 +18,55 @@ struct Derived final : Base1 {};
Derived *ptr_cast(Base1 *ptr) {
return dynamic_cast<Derived *>(ptr);
// CHECK: %[[#SRC:]] = cir.load{{.*}} %{{.+}} : !cir.ptr<!cir.ptr<!rec_Base1>>, !cir.ptr<!rec_Base1>
// CHECK-NEXT: %[[#EXPECTED_VPTR:]] = cir.vtable.address_point(@_ZTV7Derived, address_point = <index = 0, offset = 2>) : !cir.vptr
// CHECK-NEXT: %[[#SRC_VPTR_PTR:]] = cir.cast bitcast %[[#SRC]] : !cir.ptr<!rec_Base1> -> !cir.ptr<!cir.vptr>
// CHECK-NEXT: %[[#SRC_VPTR:]] = cir.load{{.*}} %[[#SRC_VPTR_PTR]] : !cir.ptr<!cir.vptr>, !cir.vptr
// CHECK-NEXT: %[[#SUCCESS:]] = cir.cmp(eq, %[[#SRC_VPTR]], %[[#EXPECTED_VPTR]]) : !cir.vptr, !cir.bool
// CHECK-NEXT: %{{.+}} = cir.ternary(%[[#SUCCESS]], true {
// CHECK-NEXT: %[[#RES:]] = cir.cast bitcast %[[#SRC]] : !cir.ptr<!rec_Base1> -> !cir.ptr<!rec_Derived>
// CHECK-NEXT: cir.yield %[[#RES]] : !cir.ptr<!rec_Derived>
// CHECK-NEXT: %[[#SRC_IS_NONNULL:]] = cir.cast ptr_to_bool %[[#SRC]] : !cir.ptr<!rec_Base1> -> !cir.bool
// CHECK-NEXT: %[[#SRC_IS_NULL:]] = cir.unary(not, %[[#SRC_IS_NONNULL]]) : !cir.bool, !cir.bool
// CHECK-NEXT: %[[#RESULT:]] = cir.ternary(%4, true {
// CHECK-NEXT: %[[#NULL_DEST_PTR:]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_Derived>
// CHECK-NEXT: cir.yield %[[#NULL_DEST_PTR]] : !cir.ptr<!rec_Derived>
// CHECK-NEXT: }, false {
// CHECK-NEXT: %[[#NULL:]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_Derived>
// CHECK-NEXT: cir.yield %[[#NULL]] : !cir.ptr<!rec_Derived>
// CHECK-NEXT: %[[#EXPECTED_VPTR:]] = cir.vtable.address_point(@_ZTV7Derived, address_point = <index = 0, offset = 2>) : !cir.vptr
// CHECK-NEXT: %[[#SRC_VPTR_PTR:]] = cir.cast bitcast %[[#SRC]] : !cir.ptr<!rec_Base1> -> !cir.ptr<!cir.vptr>
// CHECK-NEXT: %[[#SRC_VPTR:]] = cir.load{{.*}} %[[#SRC_VPTR_PTR]] : !cir.ptr<!cir.vptr>, !cir.vptr
// CHECK-NEXT: %[[#SUCCESS:]] = cir.cmp(eq, %[[#SRC_VPTR]], %[[#EXPECTED_VPTR]]) : !cir.vptr, !cir.bool
// CHECK-NEXT: %[[#EXACT_RESULT:]] = cir.ternary(%[[#SUCCESS]], true {
// CHECK-NEXT: %[[#RES:]] = cir.cast bitcast %[[#SRC]] : !cir.ptr<!rec_Base1> -> !cir.ptr<!rec_Derived>
// CHECK-NEXT: cir.yield %[[#RES]] : !cir.ptr<!rec_Derived>
// CHECK-NEXT: }, false {
// CHECK-NEXT: %[[#NULL:]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_Derived>
// CHECK-NEXT: cir.yield %[[#NULL]] : !cir.ptr<!rec_Derived>
// CHECK-NEXT: }) : (!cir.bool) -> !cir.ptr<!rec_Derived>
// CHECK-NEXT: cir.yield %[[#EXACT_RESULT]] : !cir.ptr<!rec_Derived>
// CHECK-NEXT: }) : (!cir.bool) -> !cir.ptr<!rec_Derived>
}

// LLVM: define dso_local ptr @_Z8ptr_castP5Base1(ptr readonly captures(ret: address, provenance) %[[#SRC:]])
// LLVM: define dso_local ptr @_Z8ptr_castP5Base1(ptr {{.*}} %[[#SRC:]])
// LLVM-NEXT: %[[SRC_IS_NULL:.*]] = icmp eq ptr %[[#SRC]], null
// LLVM-NEXT: br i1 %[[SRC_IS_NULL]], label %[[#LABEL_END:]], label %[[#LABEL_NONNULL:]]
// LLVM: [[#LABEL_NONNULL]]
// LLVM-NEXT: %[[#VPTR:]] = load ptr, ptr %[[#SRC]], align 8
// LLVM-NEXT: %[[#SUCCESS:]] = icmp eq ptr %[[#VPTR]], getelementptr inbounds nuw (i8, ptr @_ZTV7Derived, i64 16)
// LLVM-NEXT: %[[RESULT:.+]] = select i1 %[[#SUCCESS]], ptr %[[#SRC]], ptr null
// LLVM-NEXT: ret ptr %[[RESULT]]
// LLVM-NEXT: %[[EXACT_RESULT:.*]] = select i1 %[[#SUCCESS]], ptr %[[#SRC]], ptr null
// LLVM-NEXT: br label %[[#LABEL_END]]
// LLVM: [[#LABEL_END]]
// LLVM-NEXT: %[[#RESULT:]] = phi ptr [ %[[EXACT_RESULT]], %[[#LABEL_NONNULL]] ], [ null, %{{.*}} ]
// LLVM-NEXT: ret ptr %[[#RESULT]]
// LLVM-NEXT: }

// OGCG: define{{.*}} ptr @_Z8ptr_castP5Base1(ptr {{.*}} %[[SRC:.*]])
// OGCG-NEXT: entry:
// OGCG-NEXT: %[[NULL_CHECK:.*]] = icmp eq ptr %[[SRC]], null
// OGCG-NEXT: br i1 %[[NULL_CHECK]], label %[[LABEL_NULL:.*]], label %[[LABEL_NOTNULL:.*]]
// OGCG: [[LABEL_NOTNULL]]:
// OGCG-NEXT: %[[VTABLE:.*]] = load ptr, ptr %[[SRC]], align 8
// OGCG-NEXT: %[[VTABLE_CHECK:.*]] = icmp eq ptr %[[VTABLE]], getelementptr inbounds {{.*}} (i8, ptr @_ZTV7Derived, i64 16)
// OGCG-NEXT: br i1 %[[VTABLE_CHECK]], label %[[LABEL_END:.*]], label %[[LABEL_NULL]]
// OGCG: [[LABEL_NULL]]:
// OGCG-NEXT: br label %[[LABEL_END]]
// OGCG: [[LABEL_END]]:
// OGCG-NEXT: %[[RESULT:.*]] = phi ptr [ %[[SRC]], %[[LABEL_NOTNULL]] ], [ null, %[[LABEL_NULL]] ]
// OGCG-NEXT: ret ptr %[[RESULT]]
// OGCG-NEXT: }

Derived &ref_cast(Base1 &ref) {
return dynamic_cast<Derived &>(ref);
// CHECK: %[[#SRC:]] = cir.load{{.*}} %{{.+}} : !cir.ptr<!cir.ptr<!rec_Base1>>, !cir.ptr<!rec_Base1>
Expand All @@ -62,6 +93,18 @@ Derived &ref_cast(Base1 &ref) {
// LLVM-NEXT: ret ptr %[[#SRC]]
// LLVM-NEXT: }

// OGCG: define{{.*}} ptr @_Z8ref_castR5Base1(ptr {{.*}} %[[REF:.*]])
// OGCG-NEXT: entry:
// OGCG-NEXT: %[[VTABLE:.*]] = load ptr, ptr %[[REF]], align 8
// OGCG-NEXT: %[[VTABLE_CHECK:.*]] = icmp eq ptr %[[VTABLE]], getelementptr inbounds {{.*}} (i8, ptr @_ZTV7Derived, i64 16)
// OGCG-NEXT: br i1 %[[VTABLE_CHECK]], label %[[LABEL_END:.*]], label %[[LABEL_NULL:.*]]
// OGCG: [[LABEL_NULL]]:
// OGCG-NEXT: {{.*}}call void @__cxa_bad_cast()
// OGCG-NEXT: unreachable
// OGCG: [[LABEL_END]]:
// OGCG-NEXT: ret ptr %[[REF]]
// OGCG-NEXT: }

Derived *ptr_cast_always_fail(Base2 *ptr) {
return dynamic_cast<Derived *>(ptr);
// CHECK: %{{.+}} = cir.load{{.*}} %{{.+}} : !cir.ptr<!cir.ptr<!rec_Base2>>, !cir.ptr<!rec_Base2>
Expand Down
Loading