| // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %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-lowering-prepare %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 dso_local @_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 dso_local @_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 dso_local @_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 dso_local @_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.bool |
| // 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 dso_local @_Z20ptr_cast_to_completeP4Base |
| // CIR-BEFORE: %{{.+}} = cir.dyn_cast ptr %{{.+}} : !cir.ptr<!rec_Base> -> !cir.ptr<!void> |
| // CIR-BEFORE: } |
| |
| // CIR-AFTER: cir.func dso_local @_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]] ] |