Files
llvm-project/clang/test/CIR/CodeGen/dynamic-cast.cpp
Andy Kaylor 91cba196c4 [CIR] Fix dynamic cast of const types (#192751)
When a dynamic cast was performed using const-qualified values, we were
generating a reference to const-qualified typeinfo but never emitting
such const-qualified typeinfo, leading to an undefined reference at link
time.

This change fixes that by stripping the type qualifiers before
processing the cast. This matches the behavior of classic codegen in
ItaniumCXXABI::emitDynamicCastCall.
2026-04-20 09:23:49 -07:00

198 lines
11 KiB
C++

// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-cxxabi-lowering %s -o %t.cir 2> %t.before.log
// RUN: FileCheck %s --input-file=%t.before.log -check-prefix=CIR-BEFORE
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-cir -mmlir --mlir-print-ir-after=cir-cxxabi-lowering %s -o %t.cir 2> %t.after.log
// RUN: FileCheck %s --input-file=%t.after.log -check-prefix=CIR-AFTER
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck %s --input-file=%t-cir.ll -check-prefix=LLVM
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm %s -o %t.ll
// RUN: FileCheck %s --input-file=%t.ll -check-prefix=OGCG
struct Base {
virtual ~Base();
};
struct Derived : Base {};
// CIR-BEFORE-DAG: !rec_Base = !cir.record
// CIR-BEFORE-DAG: !rec_Derived = !cir.record
// CIR-BEFORE-DAG: #dyn_cast_info__ZTI4Base__ZTI7Derived = #cir.dyn_cast_info<src_rtti = #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>, dest_rtti = #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>, runtime_func = @__dynamic_cast, bad_cast_func = @__cxa_bad_cast, offset_hint = #cir.int<0> : !s64i>
Derived *ptr_cast(Base *b) {
return dynamic_cast<Derived *>(b);
}
// CIR-BEFORE: cir.func {{.*}} @_Z8ptr_castP4Base
// CIR-BEFORE: %{{.+}} = cir.dyn_cast ptr %{{.+}} : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived> #dyn_cast_info__ZTI4Base__ZTI7Derived
// CIR-BEFORE: }
// CIR-AFTER: cir.func {{.*}} @_Z8ptr_castP4Base
// CIR-AFTER: %[[SRC:.*]] = cir.load{{.*}} %{{.+}} : !cir.ptr<!cir.ptr<!rec_Base>>, !cir.ptr<!rec_Base>
// CIR-AFTER-NEXT: %[[SRC_IS_NOT_NULL:.*]] = cir.cast ptr_to_bool %[[SRC]] : !cir.ptr<!rec_Base> -> !cir.bool
// CIR-AFTER-NEXT: %{{.+}} = cir.ternary(%[[SRC_IS_NOT_NULL]], true {
// CIR-AFTER-NEXT: %[[SRC_VOID_PTR:.*]] = cir.cast bitcast %[[SRC]] : !cir.ptr<!rec_Base> -> !cir.ptr<!void>
// CIR-AFTER-NEXT: %[[BASE_RTTI:.*]] = cir.const #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>
// CIR-AFTER-NEXT: %[[DERIVED_RTTI:.*]] = cir.const #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>
// CIR-AFTER-NEXT: %[[HINT:.*]] = cir.const #cir.int<0> : !s64i
// CIR-AFTER-NEXT: %[[RT_CALL_RET:.*]] = cir.call @__dynamic_cast(%[[SRC_VOID_PTR]], %[[BASE_RTTI]], %[[DERIVED_RTTI]], %[[HINT]]) : (!cir.ptr<!void>, !cir.ptr<!u8i>, !cir.ptr<!u8i>, !s64i) -> !cir.ptr<!void>
// CIR-AFTER-NEXT: %[[CASTED:.*]] = cir.cast bitcast %[[RT_CALL_RET]] : !cir.ptr<!void> -> !cir.ptr<!rec_Derived>
// CIR-AFTER-NEXT: cir.yield %[[CASTED]] : !cir.ptr<!rec_Derived>
// CIR-AFTER-NEXT: }, false {
// CIR-AFTER-NEXT: %[[NULL_PTR:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_Derived>
// CIR-AFTER-NEXT: cir.yield %[[NULL_PTR]] : !cir.ptr<!rec_Derived>
// CIR-AFTER-NEXT: }) : (!cir.bool) -> !cir.ptr<!rec_Derived>
// CIR-AFTER: }
// LLVM: define {{.*}} @_Z8ptr_castP4Base
// LLVM: %[[IS_NOT_NULL:.*]] = icmp ne ptr %[[PTR:.*]], null
// LLVM: br i1 %[[IS_NOT_NULL]], label %[[NOT_NULL:.*]], label %[[NULL:.*]]
// LLVM: [[NOT_NULL]]:
// LLVM: %[[RESULT:.*]] = call ptr @__dynamic_cast(ptr %[[PTR]], ptr @_ZTI4Base, ptr @_ZTI7Derived, i64 0)
// LLVM: br label %[[DONE:.*]]
// LLVM: [[NULL]]:
// LLVM: br label %[[DONE]]
// LLVM: [[DONE]]:
// LLVM: %[[RET:.*]] = phi ptr [ null, %[[NULL]] ], [ %[[RESULT]], %[[NOT_NULL]] ]
// OGCG: define {{.*}} @_Z8ptr_castP4Base
// OGCG: %[[IS_NULL:.*]] = icmp eq ptr %[[PTR:.*]], null
// OGCG: br i1 %[[IS_NULL]], label %[[NULL:.*]], label %[[NOT_NULL:.*]]
// OGCG: [[NOT_NULL]]:
// OGCG: %[[RESULT:.*]] = call ptr @__dynamic_cast(ptr %[[PTR]], ptr @_ZTI4Base, ptr @_ZTI7Derived, i64 0)
// OGCG: br label %[[DONE:.*]]
// OGCG: [[NULL]]:
// OGCG: br label %[[DONE]]
// OGCG: [[DONE]]:
// OGCG: %[[RET:.*]] = phi ptr [ %[[RESULT]], %[[NOT_NULL]] ], [ null, %[[NULL]] ]
Derived &ref_cast(Base &b) {
return dynamic_cast<Derived &>(b);
}
// CIR-BEFORE: cir.func {{.*}} @_Z8ref_castR4Base
// CIR-BEFORE: %{{.+}} = cir.dyn_cast ref %{{.+}} : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived> #dyn_cast_info__ZTI4Base__ZTI7Derived
// CIR-BEFORE: }
// CIR-AFTER: cir.func {{.*}} @_Z8ref_castR4Base
// CIR-AFTER: %[[SRC_VOID_PTR:.*]] = cir.cast bitcast %{{.+}} : !cir.ptr<!rec_Base> -> !cir.ptr<!void>
// CIR-AFTER-NEXT: %[[SRC_RTTI:.*]] = cir.const #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>
// CIR-AFTER-NEXT: %[[DEST_RTTI:.*]] = cir.const #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>
// CIR-AFTER-NEXT: %[[OFFSET_HINT:.*]] = cir.const #cir.int<0> : !s64i
// CIR-AFTER-NEXT: %[[CASTED_PTR:.*]] = cir.call @__dynamic_cast(%[[SRC_VOID_PTR]], %[[SRC_RTTI]], %[[DEST_RTTI]], %[[OFFSET_HINT]]) : (!cir.ptr<!void>, !cir.ptr<!u8i>, !cir.ptr<!u8i>, !s64i) -> !cir.ptr<!void>
// CIR-AFTER-NEXT: %[[NULL_PTR:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
// CIR-AFTER-NEXT: %[[CASTED_PTR_IS_NULL:.*]] = cir.cmp eq %[[CASTED_PTR]], %[[NULL_PTR]] : !cir.ptr<!void>
// CIR-AFTER-NEXT: cir.if %[[CASTED_PTR_IS_NULL]] {
// CIR-AFTER-NEXT: cir.call @__cxa_bad_cast() : () -> ()
// CIR-AFTER-NEXT: cir.unreachable
// CIR-AFTER-NEXT: }
// CIR-AFTER-NEXT: %{{.+}} = cir.cast bitcast %[[CASTED_PTR]] : !cir.ptr<!void> -> !cir.ptr<!rec_Derived>
// CIR-AFTER: }
// LLVM: define {{.*}} ptr @_Z8ref_castR4Base
// LLVM: %[[RESULT:.*]] = call ptr @__dynamic_cast(ptr %{{.*}}, ptr @_ZTI4Base, ptr @_ZTI7Derived, i64 0)
// LLVM: %[[IS_NULL:.*]] = icmp eq ptr %[[RESULT]], null
// LLVM: br i1 %[[IS_NULL]], label %[[BAD_CAST:.*]], label %[[DONE:.*]]
// LLVM: [[BAD_CAST]]:
// LLVM: call void @__cxa_bad_cast()
// OGCG: define {{.*}} ptr @_Z8ref_castR4Base
// OGCG: %[[RESULT:.*]] = call ptr @__dynamic_cast(ptr %0, ptr @_ZTI4Base, ptr @_ZTI7Derived, i64 0)
// OGCG: %[[IS_NULL:.*]] = icmp eq ptr %[[RESULT]], null
// OGCG: br i1 %[[IS_NULL]], label %[[BAD_CAST:.*]], label %[[DONE:.*]]
// OGCG: [[BAD_CAST]]:
// OGCG: call void @__cxa_bad_cast()
void *ptr_cast_to_complete(Base *ptr) {
return dynamic_cast<void *>(ptr);
}
// CIR-BEFORE: cir.func {{.*}} @_Z20ptr_cast_to_completeP4Base
// CIR-BEFORE: %{{.+}} = cir.dyn_cast ptr %{{.+}} : !cir.ptr<!rec_Base> -> !cir.ptr<!void>
// CIR-BEFORE: }
// CIR-AFTER: cir.func {{.*}} @_Z20ptr_cast_to_completeP4Base
// CIR-AFTER: %[[SRC:.*]] = cir.load{{.*}} %{{.+}} : !cir.ptr<!cir.ptr<!rec_Base>>, !cir.ptr<!rec_Base>
// CIR-AFTER-NEXT: %[[SRC_IS_NOT_NULL:.*]] = cir.cast ptr_to_bool %[[SRC]] : !cir.ptr<!rec_Base> -> !cir.bool
// CIR-AFTER-NEXT: %{{.+}} = cir.ternary(%[[SRC_IS_NOT_NULL]], true {
// CIR-AFTER-NEXT: %[[VPTR_PTR:.*]] = cir.vtable.get_vptr %[[SRC]] : !cir.ptr<!rec_Base> -> !cir.ptr<!cir.vptr>
// CIR-AFTER-NEXT: %[[VPTR:.*]] = cir.load {{.*}} %[[VPTR_PTR]] : !cir.ptr<!cir.vptr>, !cir.vptr
// CIR-AFTER-NEXT: %[[ELEM_PTR:.*]] = cir.cast bitcast %[[VPTR]] : !cir.vptr -> !cir.ptr<!s64i>
// CIR-AFTER-NEXT: %[[MINUS_TWO:.*]] = cir.const #cir.int<-2> : !s64i
// CIR-AFTER-NEXT: %[[BASE_OFFSET_PTR:.*]] = cir.ptr_stride %[[ELEM_PTR]], %[[MINUS_TWO]] : (!cir.ptr<!s64i>, !s64i) -> !cir.ptr<!s64i>
// CIR-AFTER-NEXT: %[[BASE_OFFSET:.*]] = cir.load{{.*}} %[[BASE_OFFSET_PTR]] : !cir.ptr<!s64i>, !s64i
// CIR-AFTER-NEXT: %[[SRC_BYTES_PTR:.*]] = cir.cast bitcast %[[SRC]] : !cir.ptr<!rec_Base> -> !cir.ptr<!u8i>
// CIR-AFTER-NEXT: %[[DST_BYTES_PTR:.*]] = cir.ptr_stride %[[SRC_BYTES_PTR]], %[[BASE_OFFSET]] : (!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i>
// CIR-AFTER-NEXT: %[[CASTED_PTR:.*]] = cir.cast bitcast %[[DST_BYTES_PTR]] : !cir.ptr<!u8i> -> !cir.ptr<!void>
// CIR-AFTER-NEXT: cir.yield %[[CASTED_PTR]] : !cir.ptr<!void>
// CIR-AFTER-NEXT: }, false {
// CIR-AFTER-NEXT: %[[NULL_PTR:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
// CIR-AFTER-NEXT: cir.yield %[[NULL_PTR]] : !cir.ptr<!void>
// CIR-AFTER-NEXT: }) : (!cir.bool) -> !cir.ptr<!void>
// CIR-AFTER: }
// LLVM: define {{.*}} @_Z20ptr_cast_to_completeP4Base
// LLVM: %[[IS_NOT_NULL:.*]] = icmp ne ptr %[[PTR:.*]], null
// LLVM: br i1 %[[IS_NOT_NULL]], label %[[NOT_NULL:.*]], label %[[NULL:.*]]
// LLVM: [[NOT_NULL]]:
// LLVM: %[[VPTR:.*]] = load ptr, ptr %[[PTR]]
// LLVM: %[[BASE_OFFSET_PTR:.*]] = getelementptr i64, ptr %7, i64 -2
// LLVM: %[[BASE_OFFSET:.*]] = load i64, ptr %[[BASE_OFFSET_PTR]]
// LLVM: %[[RESULT:.*]] = getelementptr i8, ptr %[[PTR]], i64 %[[BASE_OFFSET]]
// LLVM: br label %[[DONE:.*]]
// LLVM: [[NULL]]:
// LLVM: br label %[[DONE]]
// LLVM: [[DONE]]:
// LLVM: %[[RET:.*]] = phi ptr [ null, %[[NULL]] ], [ %[[RESULT]], %[[NOT_NULL]] ]
// OGCG: define {{.*}} @_Z20ptr_cast_to_completeP4Base
// OGCG: %[[IS_NULL:.*]] = icmp eq ptr %[[PTR:.*]], null
// OGCG: br i1 %[[IS_NULL]], label %[[NULL:.*]], label %[[NOT_NULL:.*]]
// OGCG: [[NOT_NULL]]:
// OGCG: %[[VPTR:.*]] = load ptr, ptr %[[PTR]]
// OGCG: %[[BASE_OFFSET_PTR:.*]] = getelementptr inbounds i64, ptr %[[VPTR]], i64 -2
// OGCG: %[[BASE_OFFSET:.*]] = load i64, ptr %[[BASE_OFFSET_PTR]]
// OGCG: %[[RESULT:.*]] = getelementptr inbounds i8, ptr %[[PTR]], i64 %[[BASE_OFFSET]]
// OGCG: br label %[[DONE:.*]]
// OGCG: [[NULL]]:
// OGCG: br label %[[DONE]]
// OGCG: [[DONE]]:
// OGCG: %[[RET:.*]] = phi ptr [ %[[RESULT]], %[[NOT_NULL]] ], [ null, %[[NULL]] ]
// Dynamic cast to a const pointer/reference must reference the typeinfo of
// the unqualified class type (e.g. `_ZTI7Derived`), not a const-qualified
// typeinfo symbol (`_ZTIK7Derived`), which is never emitted. Regression test
// for a bug where CIR emitted `_ZTIK...` references that caused link-time
// undefined-reference errors.
const Derived *const_ptr_cast(const Base *b) {
return dynamic_cast<const Derived *>(b);
}
// CIR-BEFORE: cir.func {{.*}} @_Z14const_ptr_castPK4Base
// CIR-BEFORE: %{{.+}} = cir.dyn_cast ptr %{{.+}} : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived> #dyn_cast_info__ZTI4Base__ZTI7Derived
// CIR-BEFORE: }
// LLVM: define {{.*}} @_Z14const_ptr_castPK4Base
// LLVM: call ptr @__dynamic_cast(ptr %{{.*}}, ptr @_ZTI4Base, ptr @_ZTI7Derived, i64 0)
// LLVM-NOT: _ZTIK4Base
// LLVM-NOT: _ZTIK7Derived
// OGCG: define {{.*}} @_Z14const_ptr_castPK4Base
// OGCG: call ptr @__dynamic_cast(ptr %{{.*}}, ptr @_ZTI4Base, ptr @_ZTI7Derived, i64 0)
const Derived &const_ref_cast(const Base &b) {
return dynamic_cast<const Derived &>(b);
}
// CIR-BEFORE: cir.func {{.*}} @_Z14const_ref_castRK4Base
// CIR-BEFORE: %{{.+}} = cir.dyn_cast ref %{{.+}} : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived> #dyn_cast_info__ZTI4Base__ZTI7Derived
// CIR-BEFORE: }
// LLVM: define {{.*}} ptr @_Z14const_ref_castRK4Base
// LLVM: call ptr @__dynamic_cast(ptr %{{.*}}, ptr @_ZTI4Base, ptr @_ZTI7Derived, i64 0)
// LLVM-NOT: _ZTIK4Base
// LLVM-NOT: _ZTIK7Derived
// OGCG: define {{.*}} ptr @_Z14const_ref_castRK4Base
// OGCG: call ptr @__dynamic_cast(ptr %{{.*}}, ptr @_ZTI4Base, ptr @_ZTI7Derived, i64 0)