From 82c35938c1b27ed88196a5e61ed699402807e8af Mon Sep 17 00:00:00 2001 From: Sirui Mu Date: Sat, 3 Feb 2024 06:52:55 +0800 Subject: [PATCH] [CIR][CIRGen] Support dynamic_cast to void ptr (#442) This patch adds CIRGen for downcasting a pointer to the complete object through `dynamic_cast`. Together with #426 , the full functionality of `dynamic_cast` should be supported in CIRGen after this PR merges. --- clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 4 ++ clang/lib/CIR/CodeGen/CIRGenClass.cpp | 7 +-- clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp | 17 +++--- clang/lib/CIR/CodeGen/CIRGenFunction.h | 2 +- clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 55 ++++++++++++++++++- clang/test/CIR/CodeGen/dynamic-cast.cpp | 22 ++++++++ 6 files changed, 93 insertions(+), 14 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index b7dc7b66a4f3..b19cdb111ac3 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -310,6 +310,10 @@ class CIRGenCXXABI { QualType SrcRecordTy, QualType DestTy, QualType DestRecordTy) = 0; + + virtual mlir::Value buildDynamicCastToVoid(CIRGenFunction &CGF, + mlir::Location Loc, Address Value, + QualType SrcRecordTy) = 0; }; /// Creates and Itanium-family ABI diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index 73b8f922783c..4f9e06b668be 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -1461,12 +1461,11 @@ void CIRGenFunction::buildTypeMetadataCodeForVCall(const CXXRecordDecl *RD, } } -mlir::Value CIRGenFunction::getVTablePtr(SourceLocation Loc, Address This, +mlir::Value CIRGenFunction::getVTablePtr(mlir::Location Loc, Address This, mlir::Type VTableTy, const CXXRecordDecl *RD) { - auto loc = getLoc(Loc); - Address VTablePtrSrc = builder.createElementBitCast(loc, This, VTableTy); - auto VTable = builder.createLoad(loc, VTablePtrSrc); + Address VTablePtrSrc = builder.createElementBitCast(Loc, This, VTableTy); + auto VTable = builder.createLoad(Loc, VTablePtrSrc); assert(!UnimplementedFeature::tbaa()); if (CGM.getCodeGenOpts().OptimizationLevel > 0 && diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp index 7efc6220bc87..22559ce36ad5 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp @@ -944,7 +944,8 @@ mlir::Value CIRGenFunction::buildDynamicCast(Address ThisAddr, QualType srcRecordTy; QualType destRecordTy; if (isDynCastToVoid) { - llvm_unreachable("NYI"); + srcRecordTy = srcTy->getPointeeType(); + // No destRecordTy. } else if (const PointerType *DestPTy = destTy->getAs()) { srcRecordTy = srcTy->castAs()->getPointeeType(); destRecordTy = DestPTy->getPointeeType(); @@ -970,13 +971,13 @@ mlir::Value CIRGenFunction::buildDynamicCast(Address ThisAddr, auto buildDynamicCastAfterNullCheck = [&]() -> mlir::Value { if (isDynCastToVoid) - llvm_unreachable("NYI"); - else { - assert(destRecordTy->isRecordType() && - "destination type must be a record type!"); - return CGM.getCXXABI().buildDynamicCastCall( - *this, loc, ThisAddr, srcRecordTy, destTy, destRecordTy); - } + return CGM.getCXXABI().buildDynamicCastToVoid(*this, loc, ThisAddr, + srcRecordTy); + + assert(destRecordTy->isRecordType() && + "destination type must be a record type!"); + return CGM.getCXXABI().buildDynamicCastCall( + *this, loc, ThisAddr, srcRecordTy, destTy, destRecordTy); }; if (!shouldNullCheckSrcValue) diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index c736615f6f65..2cc38010808c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -1298,7 +1298,7 @@ class CIRGenFunction : public CIRGenTypeCache { const clang::CXXRecordDecl *VTableClass, VisitedVirtualBasesSetTy &VBases, VPtrsVector &vptrs); /// Return the Value of the vtable pointer member pointed to by This. - mlir::Value getVTablePtr(SourceLocation Loc, Address This, + mlir::Value getVTablePtr(mlir::Location Loc, Address This, mlir::Type VTableTy, const CXXRecordDecl *VTableClass); diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index 0f1f43b31913..336a547d8033 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -293,6 +293,10 @@ class CIRGenItaniumCXXABI : public cir::CIRGenCXXABI { QualType DestTy, QualType DestRecordTy) override; + mlir::Value buildDynamicCastToVoid(CIRGenFunction &CGF, mlir::Location Loc, + Address Value, + QualType SrcRecordTy) override; + /**************************** RTTI Uniqueness ******************************/ protected: /// Returns true if the ABI requires RTTI type_info objects to be unique @@ -844,7 +848,7 @@ CIRGenCallee CIRGenItaniumCXXABI::getVirtualFunctionPointer( auto TyPtr = CGF.getBuilder().getPointerTo(Ty); auto *MethodDecl = cast(GD.getDecl()); auto VTable = CGF.getVTablePtr( - Loc, This, CGF.getBuilder().getPointerTo(TyPtr), MethodDecl->getParent()); + loc, This, CGF.getBuilder().getPointerTo(TyPtr), MethodDecl->getParent()); uint64_t VTableIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(GD); mlir::Value VFunc{}; @@ -2328,3 +2332,52 @@ mlir::Value CIRGenItaniumCXXABI::buildDynamicCastCall( mlir::Type destCIRTy = CGF.ConvertType(DestTy); return CGF.getBuilder().createBitcast(castedPtr, destCIRTy); } + +mlir::Value CIRGenItaniumCXXABI::buildDynamicCastToVoid(CIRGenFunction &CGF, + mlir::Location Loc, + Address Value, + QualType SrcRecordTy) { + auto *clsDecl = + cast(SrcRecordTy->castAs()->getDecl()); + + // TODO(cir): consider address space in this function. + assert(!UnimplementedFeature::addressSpace()); + + auto loadOffsetToTopFromVTable = + [&](mlir::Type vtableElemTy, CharUnits vtableElemAlign) -> mlir::Value { + mlir::Type vtablePtrTy = CGF.getBuilder().getPointerTo(vtableElemTy); + mlir::Value vtablePtr = CGF.getVTablePtr(Loc, Value, vtablePtrTy, clsDecl); + + // Get the address point in the vtable that contains offset-to-top. + mlir::Value offsetToTopSlotPtr = + CGF.getBuilder().create( + Loc, vtablePtrTy, mlir::FlatSymbolRefAttr{}, vtablePtr, + /*vtable_index=*/0, -2ULL); + return CGF.getBuilder().createAlignedLoad( + Loc, vtableElemTy, offsetToTopSlotPtr, vtableElemAlign); + }; + + // Calculate the offset from the given object to its containing complete + // object. + mlir::Value offsetToTop; + if (CGM.getItaniumVTableContext().isRelativeLayout()) { + offsetToTop = loadOffsetToTopFromVTable(CGF.getBuilder().getSInt32Ty(), + CharUnits::fromQuantity(4)); + } else { + offsetToTop = loadOffsetToTopFromVTable( + CGF.convertType(CGF.getContext().getPointerDiffType()), + CGF.getPointerAlign()); + } + + // Finally, add the offset to the given pointer. + // Cast the input pointer to a uint8_t* to allow pointer arithmetic. + auto u8PtrTy = CGF.getBuilder().getUInt8PtrTy(); + mlir::Value srcBytePtr = + CGF.getBuilder().createBitcast(Value.getPointer(), u8PtrTy); + // Do the pointer arithmetic. + mlir::Value dstBytePtr = CGF.getBuilder().create( + Loc, u8PtrTy, srcBytePtr, offsetToTop); + // Cast the result to a void*. + return CGF.getBuilder().createBitcast(dstBytePtr, + CGF.getBuilder().getVoidPtrTy()); +} diff --git a/clang/test/CIR/CodeGen/dynamic-cast.cpp b/clang/test/CIR/CodeGen/dynamic-cast.cpp index 13c06f266719..52e7a3cee3d0 100644 --- a/clang/test/CIR/CodeGen/dynamic-cast.cpp +++ b/clang/test/CIR/CodeGen/dynamic-cast.cpp @@ -50,3 +50,25 @@ Derived &ref_cast(Base &b) { // CHECK-NEXT: cir.call @__cxa_bad_cast() : () -> () // CHECK-NEXT: } // CHECK-NEXT: %{{.+}} = cir.cast(bitcast, %[[#V16]] : !cir.ptr), !cir.ptr + +void *ptr_cast_to_complete(Base *ptr) { + return dynamic_cast(ptr); +} + +// CHECK: cir.func @_Z20ptr_cast_to_completeP4Base +// CHECK: %[[#V19:]] = cir.load %{{.+}} : cir.ptr >, !cir.ptr +// CHECK-NEXT: %[[#V20:]] = cir.cast(ptr_to_bool, %[[#V19]] : !cir.ptr), !cir.bool +// CHECK-NEXT: %[[#V21:]] = cir.unary(not, %[[#V20]]) : !cir.bool, !cir.bool +// CHECK-NEXT: %{{.+}} = cir.ternary(%[[#V21]], true { +// CHECK-NEXT: %[[#V22:]] = cir.const(#cir.ptr : !cir.ptr) : !cir.ptr +// CHECK-NEXT: cir.yield %[[#V22]] : !cir.ptr +// CHECK-NEXT: }, false { +// CHECK-NEXT: %[[#V23:]] = cir.cast(bitcast, %[[#V19]] : !cir.ptr), !cir.ptr> +// CHECK-NEXT: %[[#V24:]] = cir.load %[[#V23]] : cir.ptr >, !cir.ptr +// CHECK-NEXT: %[[#V25:]] = cir.vtable.address_point( %[[#V24]] : !cir.ptr, vtable_index = 0, address_point_index = -2) : cir.ptr +// CHECK-NEXT: %[[#V26:]] = cir.load %[[#V25]] : cir.ptr , !s64i +// CHECK-NEXT: %[[#V27:]] = cir.cast(bitcast, %[[#V19]] : !cir.ptr), !cir.ptr +// CHECK-NEXT: %[[#V28:]] = cir.ptr_stride(%[[#V27]] : !cir.ptr, %[[#V26]] : !s64i), !cir.ptr +// CHECK-NEXT: %[[#V29:]] = cir.cast(bitcast, %[[#V28]] : !cir.ptr), !cir.ptr +// CHECK-NEXT: cir.yield %[[#V29]] : !cir.ptr +// CHECK-NEXT: }) : (!cir.bool) -> !cir.ptr