Skip to content

Commit

Permalink
[CIR][CIRGen] Support dynamic_cast to void ptr (llvm#442)
Browse files Browse the repository at this point in the history
This patch adds CIRGen for downcasting a pointer to the complete object
through `dynamic_cast<void *>`.

Together with llvm#426 , the full functionality of `dynamic_cast` should be
supported in CIRGen after this PR merges.
  • Loading branch information
Lancern authored and lanza committed Oct 1, 2024
1 parent 140004e commit 82c3593
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 14 deletions.
4 changes: 4 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 3 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 &&
Expand Down
17 changes: 9 additions & 8 deletions clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<PointerType>()) {
srcRecordTy = srcTy->castAs<PointerType>()->getPointeeType();
destRecordTy = DestPTy->getPointeeType();
Expand All @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
55 changes: 54 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -844,7 +848,7 @@ CIRGenCallee CIRGenItaniumCXXABI::getVirtualFunctionPointer(
auto TyPtr = CGF.getBuilder().getPointerTo(Ty);
auto *MethodDecl = cast<CXXMethodDecl>(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{};
Expand Down Expand Up @@ -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<CXXRecordDecl>(SrcRecordTy->castAs<RecordType>()->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<mlir::cir::VTableAddrPointOp>(
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<mlir::cir::PtrStrideOp>(
Loc, u8PtrTy, srcBytePtr, offsetToTop);
// Cast the result to a void*.
return CGF.getBuilder().createBitcast(dstBytePtr,
CGF.getBuilder().getVoidPtrTy());
}
22 changes: 22 additions & 0 deletions clang/test/CIR/CodeGen/dynamic-cast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<!void>), !cir.ptr<!ty_22Derived22>

void *ptr_cast_to_complete(Base *ptr) {
return dynamic_cast<void *>(ptr);
}

// CHECK: cir.func @_Z20ptr_cast_to_completeP4Base
// CHECK: %[[#V19:]] = cir.load %{{.+}} : cir.ptr <!cir.ptr<!ty_22Base22>>, !cir.ptr<!ty_22Base22>
// CHECK-NEXT: %[[#V20:]] = cir.cast(ptr_to_bool, %[[#V19]] : !cir.ptr<!ty_22Base22>), !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<null> : !cir.ptr<!void>) : !cir.ptr<!void>
// CHECK-NEXT: cir.yield %[[#V22]] : !cir.ptr<!void>
// CHECK-NEXT: }, false {
// CHECK-NEXT: %[[#V23:]] = cir.cast(bitcast, %[[#V19]] : !cir.ptr<!ty_22Base22>), !cir.ptr<!cir.ptr<!s64i>>
// CHECK-NEXT: %[[#V24:]] = cir.load %[[#V23]] : cir.ptr <!cir.ptr<!s64i>>, !cir.ptr<!s64i>
// CHECK-NEXT: %[[#V25:]] = cir.vtable.address_point( %[[#V24]] : !cir.ptr<!s64i>, vtable_index = 0, address_point_index = -2) : cir.ptr <!s64i>
// CHECK-NEXT: %[[#V26:]] = cir.load %[[#V25]] : cir.ptr <!s64i>, !s64i
// CHECK-NEXT: %[[#V27:]] = cir.cast(bitcast, %[[#V19]] : !cir.ptr<!ty_22Base22>), !cir.ptr<!u8i>
// CHECK-NEXT: %[[#V28:]] = cir.ptr_stride(%[[#V27]] : !cir.ptr<!u8i>, %[[#V26]] : !s64i), !cir.ptr<!u8i>
// CHECK-NEXT: %[[#V29:]] = cir.cast(bitcast, %[[#V28]] : !cir.ptr<!u8i>), !cir.ptr<!void>
// CHECK-NEXT: cir.yield %[[#V29]] : !cir.ptr<!void>
// CHECK-NEXT: }) : (!cir.bool) -> !cir.ptr<!void>

0 comments on commit 82c3593

Please sign in to comment.