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.
198 lines
11 KiB
C++
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)
|