// 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 {{.*}} @_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.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 {{.*}} @_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]] ]
