-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[CIR] Add initial support for dynamic cast #162337
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This adds support for dynamic cast handling and generating `cir.dyn_cast` operations and `cir.dyn_cast_info` attributes. This does not include support for lowering the dynamic cast to LLVM IR, which will require changes to the LoweringPrepare pass that will be made in a future change. This also does not yet handle dynamic cast to void or exact dynamic casts.
@llvm/pr-subscribers-clangir Author: Andy Kaylor (andykaylor) ChangesThis adds support for dynamic cast handling and generating This does not include support for lowering the dynamic cast to LLVM IR, which will require changes to the LoweringPrepare pass that will be made in a future change. This also does not yet handle dynamic cast to void or exact dynamic casts. Full diff: https://github.com/llvm/llvm-project/pull/162337.diff 12 Files Affected:
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index f79580083cf9f..79cc93935fcb4 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -81,9 +81,12 @@ struct MissingFeatures {
static bool opFuncExtraAttrs() { return false; }
static bool opFuncMaybeHandleStaticInExternC() { return false; }
static bool opFuncMultipleReturnVals() { return false; }
+ static bool opFuncNoUnwind() { return false; }
static bool opFuncOperandBundles() { return false; }
static bool opFuncParameterAttributes() { return false; }
+ static bool opFuncReadOnly() { return false; }
static bool opFuncSection() { return false; }
+ static bool opFuncWillReturn() { return false; }
static bool setLLVMFunctionFEnvAttributes() { return false; }
static bool setFunctionAttributes() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 58345b45c97bc..25afe8b6b901d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -122,6 +122,11 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
return getPointerTo(cir::VPtrType::get(getContext()));
}
+ cir::FuncType getFuncType(llvm::ArrayRef<mlir::Type> params, mlir::Type retTy,
+ bool isVarArg = false) {
+ return cir::FuncType::get(params, retTy, isVarArg);
+ }
+
/// Get a CIR record kind from a AST declaration tag.
cir::RecordType::RecordKind getRecordKind(const clang::TagTypeKind kind) {
switch (kind) {
@@ -372,6 +377,15 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
return cir::BinOp::create(*this, loc, cir::BinOpKind::Div, lhs, rhs);
}
+ mlir::Value createDynCast(mlir::Location loc, mlir::Value src,
+ cir::PointerType destType, bool isRefCast,
+ cir::DynamicCastInfoAttr info) {
+ auto castKind =
+ isRefCast ? cir::DynamicCastKind::Ref : cir::DynamicCastKind::Ptr;
+ return cir::DynamicCastOp::create(*this, loc, destType, castKind, src, info,
+ /*relative_layout=*/false);
+ }
+
Address createBaseClassAddr(mlir::Location loc, Address addr,
mlir::Type destType, unsigned offset,
bool assumeNotNull) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
index 2465a68b068bf..bd708e02cb037 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
@@ -54,6 +54,12 @@ class CIRGenCXXABI {
Address thisAddr, const CXXRecordDecl *classDecl,
const CXXRecordDecl *baseClassDecl) = 0;
+ virtual mlir::Value emitDynamicCast(CIRGenFunction &cgf, mlir::Location loc,
+ QualType srcRecordTy,
+ QualType destRecordTy,
+ cir::PointerType destCIRTy,
+ bool isRefCast, Address src) = 0;
+
public:
/// Similar to AddedStructorArgs, but only notes the number of additional
/// arguments.
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index be948908e18d5..f416571181153 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1185,10 +1185,16 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) {
case CK_BuiltinFnToFnPtr:
llvm_unreachable("builtin functions are handled elsewhere");
+ case CK_Dynamic: {
+ LValue lv = emitLValue(e->getSubExpr());
+ Address v = lv.getAddress();
+ const auto *dce = cast<CXXDynamicCastExpr>(e);
+ return makeNaturalAlignAddrLValue(emitDynamicCast(v, dce), e->getType());
+ }
+
// These are never l-values; just use the aggregate emission code.
case CK_NonAtomicToAtomic:
case CK_AtomicToNonAtomic:
- case CK_Dynamic:
case CK_ToUnion:
case CK_BaseToDerived:
case CK_AddressSpaceConversion:
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 4eb8ca8b5b397..9a4f1d92aa8f1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -728,3 +728,43 @@ void CIRGenFunction::emitDeleteCall(const FunctionDecl *deleteFD,
// Emit the call to delete.
emitNewDeleteCall(*this, deleteFD, deleteFTy, deleteArgs);
}
+
+mlir::Value CIRGenFunction::emitDynamicCast(Address thisAddr,
+ const CXXDynamicCastExpr *dce) {
+ mlir::Location loc = getLoc(dce->getSourceRange());
+
+ cgm.emitExplicitCastExprType(dce, this);
+ QualType destTy = dce->getTypeAsWritten();
+ QualType srcTy = dce->getSubExpr()->getType();
+
+ // C++ [expr.dynamic.cast]p7:
+ // If T is "pointer to cv void," then the result is a pointer to the most
+ // derived object pointed to by v.
+ bool isDynCastToVoid = destTy->isVoidPointerType();
+ bool isRefCast = destTy->isReferenceType();
+
+ QualType srcRecordTy;
+ QualType destRecordTy;
+ if (isDynCastToVoid) {
+ srcRecordTy = srcTy->getPointeeType();
+ // No destRecordTy.
+ } else if (const PointerType *destPTy = destTy->getAs<PointerType>()) {
+ srcRecordTy = srcTy->castAs<PointerType>()->getPointeeType();
+ destRecordTy = destPTy->getPointeeType();
+ } else {
+ srcRecordTy = srcTy;
+ destRecordTy = destTy->castAs<ReferenceType>()->getPointeeType();
+ }
+
+ assert(srcRecordTy->isRecordType() && "source type must be a record type!");
+ assert(!cir::MissingFeatures::emitTypeCheck());
+
+ if (dce->isAlwaysNull()) {
+ cgm.errorNYI(dce->getSourceRange(), "emitDynamicCastToNull");
+ return {};
+ }
+
+ auto destCirTy = mlir::cast<cir::PointerType>(convertType(destTy));
+ return cgm.getCXXABI().emitDynamicCast(*this, loc, srcRecordTy, destRecordTy,
+ destCirTy, isRefCast, thisAddr);
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 5d3496a637268..ed8714e46f155 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -1894,6 +1894,11 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) {
return v;
}
+ case CK_Dynamic: {
+ Address v = cgf.emitPointerWithAlignment(subExpr);
+ const auto *dce = cast<CXXDynamicCastExpr>(ce);
+ return cgf.emitDynamicCast(v, dce);
+ }
case CK_ArrayToPointerDecay:
return cgf.emitArrayToPointerDecay(subExpr).getPointer();
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index a60efe11f9630..88aef74ba0b0e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1285,6 +1285,8 @@ class CIRGenFunction : public CIRGenTypeCache {
mlir::LogicalResult emitDoStmt(const clang::DoStmt &s);
+ mlir::Value emitDynamicCast(Address thisAddr, const CXXDynamicCastExpr *dce);
+
/// Emit an expression as an initializer for an object (variable, field, etc.)
/// at the given location. The expression is not necessarily the normal
/// initializer for the object, and the address is not necessarily
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 04181740ccf6e..471d996c03fac 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -116,6 +116,16 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
Address thisAddr, const CXXRecordDecl *classDecl,
const CXXRecordDecl *baseClassDecl) override;
+ // The traditional clang CodeGen emits calls to `__dynamic_cast` directly into
+ // LLVM in the `emitDynamicCastCall` function. In CIR, `dynamic_cast`
+ // expressions are lowered to `cir.dyn_cast` ops instead of calls to runtime
+ // functions. So during CIRGen we don't need the `emitDynamicCastCall`
+ // function that clang CodeGen has.
+ mlir::Value emitDynamicCast(CIRGenFunction &cgf, mlir::Location loc,
+ QualType srcRecordTy, QualType destRecordTy,
+ cir::PointerType destCIRTy, bool isRefCast,
+ Address src) override;
+
/**************************** RTTI Uniqueness ******************************/
protected:
/// Returns true if the ABI requires RTTI type_info objects to be unique
@@ -1796,3 +1806,143 @@ mlir::Value CIRGenItaniumCXXABI::getVirtualBaseClassOffset(
}
return vbaseOffset;
}
+
+static cir::FuncOp getBadCastFn(CIRGenFunction &cgf) {
+ // Prototype: void __cxa_bad_cast();
+
+ // TODO(cir): set the calling convention of the runtime function.
+ assert(!cir::MissingFeatures::opFuncCallingConv());
+
+ cir::FuncType fnTy =
+ cgf.getBuilder().getFuncType({}, cgf.getBuilder().getVoidTy());
+ return cgf.cgm.createRuntimeFunction(fnTy, "__cxa_bad_cast");
+}
+
+// TODO(cir): This could be shared with classic codegen.
+static CharUnits computeOffsetHint(ASTContext &astContext,
+ const CXXRecordDecl *src,
+ const CXXRecordDecl *dst) {
+ CXXBasePaths paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
+ /*DetectVirtual=*/false);
+
+ // If Dst is not derived from Src we can skip the whole computation below and
+ // return that Src is not a public base of Dst. Record all inheritance paths.
+ if (!dst->isDerivedFrom(src, paths))
+ return CharUnits::fromQuantity(-2ULL);
+
+ unsigned numPublicPaths = 0;
+ CharUnits offset;
+
+ // Now walk all possible inheritance paths.
+ for (const CXXBasePath &path : paths) {
+ if (path.Access != AS_public) // Ignore non-public inheritance.
+ continue;
+
+ ++numPublicPaths;
+
+ for (const CXXBasePathElement &pathElement : path) {
+ // If the path contains a virtual base class we can't give any hint.
+ // -1: no hint.
+ if (pathElement.Base->isVirtual())
+ return CharUnits::fromQuantity(-1ULL);
+
+ if (numPublicPaths > 1) // Won't use offsets, skip computation.
+ continue;
+
+ // Accumulate the base class offsets.
+ const ASTRecordLayout &L =
+ astContext.getASTRecordLayout(pathElement.Class);
+ offset += L.getBaseClassOffset(
+ pathElement.Base->getType()->getAsCXXRecordDecl());
+ }
+ }
+
+ // -2: Src is not a public base of Dst.
+ if (numPublicPaths == 0)
+ return CharUnits::fromQuantity(-2ULL);
+
+ // -3: Src is a multiple public base type but never a virtual base type.
+ if (numPublicPaths > 1)
+ return CharUnits::fromQuantity(-3ULL);
+
+ // Otherwise, the Src type is a unique public nonvirtual base type of Dst.
+ // Return the offset of Src from the origin of Dst.
+ return offset;
+}
+
+static cir::FuncOp getItaniumDynamicCastFn(CIRGenFunction &cgf) {
+ // Prototype:
+ // void *__dynamic_cast(const void *sub,
+ // global_as const abi::__class_type_info *src,
+ // global_as const abi::__class_type_info *dst,
+ // std::ptrdiff_t src2dst_offset);
+
+ mlir::Type voidPtrTy = cgf.getBuilder().getVoidPtrTy();
+ mlir::Type rttiPtrTy = cgf.getBuilder().getUInt8PtrTy();
+ mlir::Type ptrDiffTy = cgf.convertType(cgf.getContext().getPointerDiffType());
+
+ // TODO(cir): mark the function as nowind willreturn readonly.
+ assert(!cir::MissingFeatures::opFuncNoUnwind());
+ assert(!cir::MissingFeatures::opFuncWillReturn());
+ assert(!cir::MissingFeatures::opFuncReadOnly());
+
+ // TODO(cir): set the calling convention of the runtime function.
+ assert(!cir::MissingFeatures::opFuncCallingConv());
+
+ cir::FuncType FTy = cgf.getBuilder().getFuncType(
+ {voidPtrTy, rttiPtrTy, rttiPtrTy, ptrDiffTy}, voidPtrTy);
+ return cgf.cgm.createRuntimeFunction(FTy, "__dynamic_cast");
+}
+
+static cir::DynamicCastInfoAttr emitDynamicCastInfo(CIRGenFunction &cgf,
+ mlir::Location loc,
+ QualType srcRecordTy,
+ QualType destRecordTy) {
+ auto srcRtti = mlir::cast<cir::GlobalViewAttr>(
+ cgf.cgm.getAddrOfRTTIDescriptor(loc, srcRecordTy));
+ auto destRtti = mlir::cast<cir::GlobalViewAttr>(
+ cgf.cgm.getAddrOfRTTIDescriptor(loc, destRecordTy));
+
+ cir::FuncOp runtimeFuncOp = getItaniumDynamicCastFn(cgf);
+ cir::FuncOp badCastFuncOp = getBadCastFn(cgf);
+ auto runtimeFuncRef = mlir::FlatSymbolRefAttr::get(runtimeFuncOp);
+ auto badCastFuncRef = mlir::FlatSymbolRefAttr::get(badCastFuncOp);
+
+ const CXXRecordDecl *srcDecl = srcRecordTy->getAsCXXRecordDecl();
+ const CXXRecordDecl *destDecl = destRecordTy->getAsCXXRecordDecl();
+ CharUnits offsetHint = computeOffsetHint(cgf.getContext(), srcDecl, destDecl);
+
+ mlir::Type ptrdiffTy = cgf.convertType(cgf.getContext().getPointerDiffType());
+ auto offsetHintAttr = cir::IntAttr::get(ptrdiffTy, offsetHint.getQuantity());
+
+ return cir::DynamicCastInfoAttr::get(srcRtti, destRtti, runtimeFuncRef,
+ badCastFuncRef, offsetHintAttr);
+}
+
+mlir::Value CIRGenItaniumCXXABI::emitDynamicCast(CIRGenFunction &cgf,
+ mlir::Location loc,
+ QualType srcRecordTy,
+ QualType destRecordTy,
+ cir::PointerType destCIRTy,
+ bool isRefCast, Address src) {
+ bool isCastToVoid = destRecordTy.isNull();
+ assert((!isCastToVoid || !isRefCast) && "cannot cast to void reference");
+
+ if (isCastToVoid) {
+ cgm.errorNYI(loc, "emitDynamicCastToVoid");
+ return {};
+ }
+
+ // If the destination is effectively final, the cast succeeds if and only
+ // if the dynamic type of the pointer is exactly the destination type.
+ if (destRecordTy->getAsCXXRecordDecl()->isEffectivelyFinal() &&
+ cgf.cgm.getCodeGenOpts().OptimizationLevel > 0) {
+ cgm.errorNYI(loc, "emitExactDynamicCast");
+ return {};
+ }
+
+ cir::DynamicCastInfoAttr castInfo =
+ emitDynamicCastInfo(cgf, loc, srcRecordTy, destRecordTy);
+ return cgf.getBuilder().createDynCast(loc, src.getPointer(), destCIRTy,
+ isRefCast, castInfo);
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 910c8a9b8f98a..fe1ea5617b8cd 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2079,6 +2079,29 @@ CIRGenModule::createCIRBuiltinFunction(mlir::Location loc, StringRef name,
return fnOp;
}
+cir::FuncOp CIRGenModule::createRuntimeFunction(cir::FuncType ty,
+ StringRef name, mlir::ArrayAttr,
+ [[maybe_unused]] bool isLocal,
+ bool assumeConvergent) {
+ if (assumeConvergent)
+ errorNYI("createRuntimeFunction: assumeConvergent");
+ if (isLocal)
+ errorNYI("createRuntimeFunction: local");
+
+ cir::FuncOp entry = getOrCreateCIRFunction(name, ty, GlobalDecl(),
+ /*forVtable=*/false);
+
+ if (entry) {
+ // TODO(cir): set the attributes of the function.
+ assert(!cir::MissingFeatures::setLLVMFunctionFEnvAttributes());
+ assert(!cir::MissingFeatures::opFuncCallingConv());
+ assert(!cir::MissingFeatures::opGlobalDLLImportExport());
+ entry.setDSOLocal(true);
+ }
+
+ return entry;
+}
+
mlir::SymbolTable::Visibility
CIRGenModule::getMLIRVisibility(cir::GlobalOp op) {
// MLIR doesn't accept public symbols declarations (only
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index c6a6681021d47..f627bae9f87f9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -480,6 +480,10 @@ class CIRGenModule : public CIRGenTypeCache {
cir::FuncType ty,
const clang::FunctionDecl *fd);
+ cir::FuncOp createRuntimeFunction(cir::FuncType ty, llvm::StringRef name,
+ mlir::ArrayAttr = {}, bool isLocal = false,
+ bool assumeConvergent = false);
+
static constexpr const char *builtinCoroId = "__builtin_coro_id";
/// Given a builtin id for a function like "__builtin_fabsf", return a
diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
index 94d856b41b3ce..84f59773757b5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
@@ -327,9 +327,40 @@ cir::GlobalLinkageKind CIRGenModule::getVTableLinkage(const CXXRecordDecl *rd) {
llvm_unreachable("Should not have been asked to emit this");
}
}
+ // -fapple-kext mode does not support weak linkage, so we must use
+ // internal linkage.
+ if (astContext.getLangOpts().AppleKext)
+ return cir::GlobalLinkageKind::InternalLinkage;
+
+ auto discardableODRLinkage = cir::GlobalLinkageKind::LinkOnceODRLinkage;
+ auto nonDiscardableODRLinkage = cir::GlobalLinkageKind::WeakODRLinkage;
+ if (rd->hasAttr<DLLExportAttr>()) {
+ // Cannot discard exported vtables.
+ discardableODRLinkage = nonDiscardableODRLinkage;
+ } else if (rd->hasAttr<DLLImportAttr>()) {
+ // Imported vtables are available externally.
+ discardableODRLinkage = cir::GlobalLinkageKind::AvailableExternallyLinkage;
+ nonDiscardableODRLinkage =
+ cir::GlobalLinkageKind::AvailableExternallyLinkage;
+ }
+
+ switch (rd->getTemplateSpecializationKind()) {
+ case TSK_Undeclared:
+ case TSK_ExplicitSpecialization:
+ case TSK_ImplicitInstantiation:
+ return discardableODRLinkage;
+
+ case TSK_ExplicitInstantiationDeclaration: {
+ errorNYI(rd->getSourceRange(),
+ "getVTableLinkage: explicit instantiation declaration");
+ return cir::GlobalLinkageKind::ExternalLinkage;
+ }
+
+ case TSK_ExplicitInstantiationDefinition:
+ return nonDiscardableODRLinkage;
+ }
- errorNYI(rd->getSourceRange(), "getVTableLinkage: no key function");
- return cir::GlobalLinkageKind::ExternalLinkage;
+ llvm_unreachable("Invalid TemplateSpecializationKind!");
}
cir::GlobalOp CIRGenVTables::getAddrOfVTT(const CXXRecordDecl *rd) {
diff --git a/clang/test/CIR/CodeGen/dynamic-cast.cpp b/clang/test/CIR/CodeGen/dynamic-cast.cpp
new file mode 100644
index 0000000000000..e5244b220e76c
--- /dev/null
+++ b/clang/test/CIR/CodeGen/dynamic-cast.cpp
@@ -0,0 +1,28 @@
+// 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
+
+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: }
+
+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: }
|
@llvm/pr-subscribers-clang Author: Andy Kaylor (andykaylor) ChangesThis adds support for dynamic cast handling and generating This does not include support for lowering the dynamic cast to LLVM IR, which will require changes to the LoweringPrepare pass that will be made in a future change. This also does not yet handle dynamic cast to void or exact dynamic casts. Full diff: https://github.com/llvm/llvm-project/pull/162337.diff 12 Files Affected:
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index f79580083cf9f..79cc93935fcb4 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -81,9 +81,12 @@ struct MissingFeatures {
static bool opFuncExtraAttrs() { return false; }
static bool opFuncMaybeHandleStaticInExternC() { return false; }
static bool opFuncMultipleReturnVals() { return false; }
+ static bool opFuncNoUnwind() { return false; }
static bool opFuncOperandBundles() { return false; }
static bool opFuncParameterAttributes() { return false; }
+ static bool opFuncReadOnly() { return false; }
static bool opFuncSection() { return false; }
+ static bool opFuncWillReturn() { return false; }
static bool setLLVMFunctionFEnvAttributes() { return false; }
static bool setFunctionAttributes() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 58345b45c97bc..25afe8b6b901d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -122,6 +122,11 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
return getPointerTo(cir::VPtrType::get(getContext()));
}
+ cir::FuncType getFuncType(llvm::ArrayRef<mlir::Type> params, mlir::Type retTy,
+ bool isVarArg = false) {
+ return cir::FuncType::get(params, retTy, isVarArg);
+ }
+
/// Get a CIR record kind from a AST declaration tag.
cir::RecordType::RecordKind getRecordKind(const clang::TagTypeKind kind) {
switch (kind) {
@@ -372,6 +377,15 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
return cir::BinOp::create(*this, loc, cir::BinOpKind::Div, lhs, rhs);
}
+ mlir::Value createDynCast(mlir::Location loc, mlir::Value src,
+ cir::PointerType destType, bool isRefCast,
+ cir::DynamicCastInfoAttr info) {
+ auto castKind =
+ isRefCast ? cir::DynamicCastKind::Ref : cir::DynamicCastKind::Ptr;
+ return cir::DynamicCastOp::create(*this, loc, destType, castKind, src, info,
+ /*relative_layout=*/false);
+ }
+
Address createBaseClassAddr(mlir::Location loc, Address addr,
mlir::Type destType, unsigned offset,
bool assumeNotNull) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
index 2465a68b068bf..bd708e02cb037 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
@@ -54,6 +54,12 @@ class CIRGenCXXABI {
Address thisAddr, const CXXRecordDecl *classDecl,
const CXXRecordDecl *baseClassDecl) = 0;
+ virtual mlir::Value emitDynamicCast(CIRGenFunction &cgf, mlir::Location loc,
+ QualType srcRecordTy,
+ QualType destRecordTy,
+ cir::PointerType destCIRTy,
+ bool isRefCast, Address src) = 0;
+
public:
/// Similar to AddedStructorArgs, but only notes the number of additional
/// arguments.
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index be948908e18d5..f416571181153 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1185,10 +1185,16 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) {
case CK_BuiltinFnToFnPtr:
llvm_unreachable("builtin functions are handled elsewhere");
+ case CK_Dynamic: {
+ LValue lv = emitLValue(e->getSubExpr());
+ Address v = lv.getAddress();
+ const auto *dce = cast<CXXDynamicCastExpr>(e);
+ return makeNaturalAlignAddrLValue(emitDynamicCast(v, dce), e->getType());
+ }
+
// These are never l-values; just use the aggregate emission code.
case CK_NonAtomicToAtomic:
case CK_AtomicToNonAtomic:
- case CK_Dynamic:
case CK_ToUnion:
case CK_BaseToDerived:
case CK_AddressSpaceConversion:
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 4eb8ca8b5b397..9a4f1d92aa8f1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -728,3 +728,43 @@ void CIRGenFunction::emitDeleteCall(const FunctionDecl *deleteFD,
// Emit the call to delete.
emitNewDeleteCall(*this, deleteFD, deleteFTy, deleteArgs);
}
+
+mlir::Value CIRGenFunction::emitDynamicCast(Address thisAddr,
+ const CXXDynamicCastExpr *dce) {
+ mlir::Location loc = getLoc(dce->getSourceRange());
+
+ cgm.emitExplicitCastExprType(dce, this);
+ QualType destTy = dce->getTypeAsWritten();
+ QualType srcTy = dce->getSubExpr()->getType();
+
+ // C++ [expr.dynamic.cast]p7:
+ // If T is "pointer to cv void," then the result is a pointer to the most
+ // derived object pointed to by v.
+ bool isDynCastToVoid = destTy->isVoidPointerType();
+ bool isRefCast = destTy->isReferenceType();
+
+ QualType srcRecordTy;
+ QualType destRecordTy;
+ if (isDynCastToVoid) {
+ srcRecordTy = srcTy->getPointeeType();
+ // No destRecordTy.
+ } else if (const PointerType *destPTy = destTy->getAs<PointerType>()) {
+ srcRecordTy = srcTy->castAs<PointerType>()->getPointeeType();
+ destRecordTy = destPTy->getPointeeType();
+ } else {
+ srcRecordTy = srcTy;
+ destRecordTy = destTy->castAs<ReferenceType>()->getPointeeType();
+ }
+
+ assert(srcRecordTy->isRecordType() && "source type must be a record type!");
+ assert(!cir::MissingFeatures::emitTypeCheck());
+
+ if (dce->isAlwaysNull()) {
+ cgm.errorNYI(dce->getSourceRange(), "emitDynamicCastToNull");
+ return {};
+ }
+
+ auto destCirTy = mlir::cast<cir::PointerType>(convertType(destTy));
+ return cgm.getCXXABI().emitDynamicCast(*this, loc, srcRecordTy, destRecordTy,
+ destCirTy, isRefCast, thisAddr);
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 5d3496a637268..ed8714e46f155 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -1894,6 +1894,11 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) {
return v;
}
+ case CK_Dynamic: {
+ Address v = cgf.emitPointerWithAlignment(subExpr);
+ const auto *dce = cast<CXXDynamicCastExpr>(ce);
+ return cgf.emitDynamicCast(v, dce);
+ }
case CK_ArrayToPointerDecay:
return cgf.emitArrayToPointerDecay(subExpr).getPointer();
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index a60efe11f9630..88aef74ba0b0e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1285,6 +1285,8 @@ class CIRGenFunction : public CIRGenTypeCache {
mlir::LogicalResult emitDoStmt(const clang::DoStmt &s);
+ mlir::Value emitDynamicCast(Address thisAddr, const CXXDynamicCastExpr *dce);
+
/// Emit an expression as an initializer for an object (variable, field, etc.)
/// at the given location. The expression is not necessarily the normal
/// initializer for the object, and the address is not necessarily
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 04181740ccf6e..471d996c03fac 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -116,6 +116,16 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
Address thisAddr, const CXXRecordDecl *classDecl,
const CXXRecordDecl *baseClassDecl) override;
+ // The traditional clang CodeGen emits calls to `__dynamic_cast` directly into
+ // LLVM in the `emitDynamicCastCall` function. In CIR, `dynamic_cast`
+ // expressions are lowered to `cir.dyn_cast` ops instead of calls to runtime
+ // functions. So during CIRGen we don't need the `emitDynamicCastCall`
+ // function that clang CodeGen has.
+ mlir::Value emitDynamicCast(CIRGenFunction &cgf, mlir::Location loc,
+ QualType srcRecordTy, QualType destRecordTy,
+ cir::PointerType destCIRTy, bool isRefCast,
+ Address src) override;
+
/**************************** RTTI Uniqueness ******************************/
protected:
/// Returns true if the ABI requires RTTI type_info objects to be unique
@@ -1796,3 +1806,143 @@ mlir::Value CIRGenItaniumCXXABI::getVirtualBaseClassOffset(
}
return vbaseOffset;
}
+
+static cir::FuncOp getBadCastFn(CIRGenFunction &cgf) {
+ // Prototype: void __cxa_bad_cast();
+
+ // TODO(cir): set the calling convention of the runtime function.
+ assert(!cir::MissingFeatures::opFuncCallingConv());
+
+ cir::FuncType fnTy =
+ cgf.getBuilder().getFuncType({}, cgf.getBuilder().getVoidTy());
+ return cgf.cgm.createRuntimeFunction(fnTy, "__cxa_bad_cast");
+}
+
+// TODO(cir): This could be shared with classic codegen.
+static CharUnits computeOffsetHint(ASTContext &astContext,
+ const CXXRecordDecl *src,
+ const CXXRecordDecl *dst) {
+ CXXBasePaths paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
+ /*DetectVirtual=*/false);
+
+ // If Dst is not derived from Src we can skip the whole computation below and
+ // return that Src is not a public base of Dst. Record all inheritance paths.
+ if (!dst->isDerivedFrom(src, paths))
+ return CharUnits::fromQuantity(-2ULL);
+
+ unsigned numPublicPaths = 0;
+ CharUnits offset;
+
+ // Now walk all possible inheritance paths.
+ for (const CXXBasePath &path : paths) {
+ if (path.Access != AS_public) // Ignore non-public inheritance.
+ continue;
+
+ ++numPublicPaths;
+
+ for (const CXXBasePathElement &pathElement : path) {
+ // If the path contains a virtual base class we can't give any hint.
+ // -1: no hint.
+ if (pathElement.Base->isVirtual())
+ return CharUnits::fromQuantity(-1ULL);
+
+ if (numPublicPaths > 1) // Won't use offsets, skip computation.
+ continue;
+
+ // Accumulate the base class offsets.
+ const ASTRecordLayout &L =
+ astContext.getASTRecordLayout(pathElement.Class);
+ offset += L.getBaseClassOffset(
+ pathElement.Base->getType()->getAsCXXRecordDecl());
+ }
+ }
+
+ // -2: Src is not a public base of Dst.
+ if (numPublicPaths == 0)
+ return CharUnits::fromQuantity(-2ULL);
+
+ // -3: Src is a multiple public base type but never a virtual base type.
+ if (numPublicPaths > 1)
+ return CharUnits::fromQuantity(-3ULL);
+
+ // Otherwise, the Src type is a unique public nonvirtual base type of Dst.
+ // Return the offset of Src from the origin of Dst.
+ return offset;
+}
+
+static cir::FuncOp getItaniumDynamicCastFn(CIRGenFunction &cgf) {
+ // Prototype:
+ // void *__dynamic_cast(const void *sub,
+ // global_as const abi::__class_type_info *src,
+ // global_as const abi::__class_type_info *dst,
+ // std::ptrdiff_t src2dst_offset);
+
+ mlir::Type voidPtrTy = cgf.getBuilder().getVoidPtrTy();
+ mlir::Type rttiPtrTy = cgf.getBuilder().getUInt8PtrTy();
+ mlir::Type ptrDiffTy = cgf.convertType(cgf.getContext().getPointerDiffType());
+
+ // TODO(cir): mark the function as nowind willreturn readonly.
+ assert(!cir::MissingFeatures::opFuncNoUnwind());
+ assert(!cir::MissingFeatures::opFuncWillReturn());
+ assert(!cir::MissingFeatures::opFuncReadOnly());
+
+ // TODO(cir): set the calling convention of the runtime function.
+ assert(!cir::MissingFeatures::opFuncCallingConv());
+
+ cir::FuncType FTy = cgf.getBuilder().getFuncType(
+ {voidPtrTy, rttiPtrTy, rttiPtrTy, ptrDiffTy}, voidPtrTy);
+ return cgf.cgm.createRuntimeFunction(FTy, "__dynamic_cast");
+}
+
+static cir::DynamicCastInfoAttr emitDynamicCastInfo(CIRGenFunction &cgf,
+ mlir::Location loc,
+ QualType srcRecordTy,
+ QualType destRecordTy) {
+ auto srcRtti = mlir::cast<cir::GlobalViewAttr>(
+ cgf.cgm.getAddrOfRTTIDescriptor(loc, srcRecordTy));
+ auto destRtti = mlir::cast<cir::GlobalViewAttr>(
+ cgf.cgm.getAddrOfRTTIDescriptor(loc, destRecordTy));
+
+ cir::FuncOp runtimeFuncOp = getItaniumDynamicCastFn(cgf);
+ cir::FuncOp badCastFuncOp = getBadCastFn(cgf);
+ auto runtimeFuncRef = mlir::FlatSymbolRefAttr::get(runtimeFuncOp);
+ auto badCastFuncRef = mlir::FlatSymbolRefAttr::get(badCastFuncOp);
+
+ const CXXRecordDecl *srcDecl = srcRecordTy->getAsCXXRecordDecl();
+ const CXXRecordDecl *destDecl = destRecordTy->getAsCXXRecordDecl();
+ CharUnits offsetHint = computeOffsetHint(cgf.getContext(), srcDecl, destDecl);
+
+ mlir::Type ptrdiffTy = cgf.convertType(cgf.getContext().getPointerDiffType());
+ auto offsetHintAttr = cir::IntAttr::get(ptrdiffTy, offsetHint.getQuantity());
+
+ return cir::DynamicCastInfoAttr::get(srcRtti, destRtti, runtimeFuncRef,
+ badCastFuncRef, offsetHintAttr);
+}
+
+mlir::Value CIRGenItaniumCXXABI::emitDynamicCast(CIRGenFunction &cgf,
+ mlir::Location loc,
+ QualType srcRecordTy,
+ QualType destRecordTy,
+ cir::PointerType destCIRTy,
+ bool isRefCast, Address src) {
+ bool isCastToVoid = destRecordTy.isNull();
+ assert((!isCastToVoid || !isRefCast) && "cannot cast to void reference");
+
+ if (isCastToVoid) {
+ cgm.errorNYI(loc, "emitDynamicCastToVoid");
+ return {};
+ }
+
+ // If the destination is effectively final, the cast succeeds if and only
+ // if the dynamic type of the pointer is exactly the destination type.
+ if (destRecordTy->getAsCXXRecordDecl()->isEffectivelyFinal() &&
+ cgf.cgm.getCodeGenOpts().OptimizationLevel > 0) {
+ cgm.errorNYI(loc, "emitExactDynamicCast");
+ return {};
+ }
+
+ cir::DynamicCastInfoAttr castInfo =
+ emitDynamicCastInfo(cgf, loc, srcRecordTy, destRecordTy);
+ return cgf.getBuilder().createDynCast(loc, src.getPointer(), destCIRTy,
+ isRefCast, castInfo);
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 910c8a9b8f98a..fe1ea5617b8cd 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2079,6 +2079,29 @@ CIRGenModule::createCIRBuiltinFunction(mlir::Location loc, StringRef name,
return fnOp;
}
+cir::FuncOp CIRGenModule::createRuntimeFunction(cir::FuncType ty,
+ StringRef name, mlir::ArrayAttr,
+ [[maybe_unused]] bool isLocal,
+ bool assumeConvergent) {
+ if (assumeConvergent)
+ errorNYI("createRuntimeFunction: assumeConvergent");
+ if (isLocal)
+ errorNYI("createRuntimeFunction: local");
+
+ cir::FuncOp entry = getOrCreateCIRFunction(name, ty, GlobalDecl(),
+ /*forVtable=*/false);
+
+ if (entry) {
+ // TODO(cir): set the attributes of the function.
+ assert(!cir::MissingFeatures::setLLVMFunctionFEnvAttributes());
+ assert(!cir::MissingFeatures::opFuncCallingConv());
+ assert(!cir::MissingFeatures::opGlobalDLLImportExport());
+ entry.setDSOLocal(true);
+ }
+
+ return entry;
+}
+
mlir::SymbolTable::Visibility
CIRGenModule::getMLIRVisibility(cir::GlobalOp op) {
// MLIR doesn't accept public symbols declarations (only
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index c6a6681021d47..f627bae9f87f9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -480,6 +480,10 @@ class CIRGenModule : public CIRGenTypeCache {
cir::FuncType ty,
const clang::FunctionDecl *fd);
+ cir::FuncOp createRuntimeFunction(cir::FuncType ty, llvm::StringRef name,
+ mlir::ArrayAttr = {}, bool isLocal = false,
+ bool assumeConvergent = false);
+
static constexpr const char *builtinCoroId = "__builtin_coro_id";
/// Given a builtin id for a function like "__builtin_fabsf", return a
diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
index 94d856b41b3ce..84f59773757b5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
@@ -327,9 +327,40 @@ cir::GlobalLinkageKind CIRGenModule::getVTableLinkage(const CXXRecordDecl *rd) {
llvm_unreachable("Should not have been asked to emit this");
}
}
+ // -fapple-kext mode does not support weak linkage, so we must use
+ // internal linkage.
+ if (astContext.getLangOpts().AppleKext)
+ return cir::GlobalLinkageKind::InternalLinkage;
+
+ auto discardableODRLinkage = cir::GlobalLinkageKind::LinkOnceODRLinkage;
+ auto nonDiscardableODRLinkage = cir::GlobalLinkageKind::WeakODRLinkage;
+ if (rd->hasAttr<DLLExportAttr>()) {
+ // Cannot discard exported vtables.
+ discardableODRLinkage = nonDiscardableODRLinkage;
+ } else if (rd->hasAttr<DLLImportAttr>()) {
+ // Imported vtables are available externally.
+ discardableODRLinkage = cir::GlobalLinkageKind::AvailableExternallyLinkage;
+ nonDiscardableODRLinkage =
+ cir::GlobalLinkageKind::AvailableExternallyLinkage;
+ }
+
+ switch (rd->getTemplateSpecializationKind()) {
+ case TSK_Undeclared:
+ case TSK_ExplicitSpecialization:
+ case TSK_ImplicitInstantiation:
+ return discardableODRLinkage;
+
+ case TSK_ExplicitInstantiationDeclaration: {
+ errorNYI(rd->getSourceRange(),
+ "getVTableLinkage: explicit instantiation declaration");
+ return cir::GlobalLinkageKind::ExternalLinkage;
+ }
+
+ case TSK_ExplicitInstantiationDefinition:
+ return nonDiscardableODRLinkage;
+ }
- errorNYI(rd->getSourceRange(), "getVTableLinkage: no key function");
- return cir::GlobalLinkageKind::ExternalLinkage;
+ llvm_unreachable("Invalid TemplateSpecializationKind!");
}
cir::GlobalOp CIRGenVTables::getAddrOfVTT(const CXXRecordDecl *rd) {
diff --git a/clang/test/CIR/CodeGen/dynamic-cast.cpp b/clang/test/CIR/CodeGen/dynamic-cast.cpp
new file mode 100644
index 0000000000000..e5244b220e76c
--- /dev/null
+++ b/clang/test/CIR/CodeGen/dynamic-cast.cpp
@@ -0,0 +1,28 @@
+// 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
+
+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: }
+
+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: }
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
|
||
// 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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not related to this PR but caught my attention, seems like offset_hint
can be simplified to use a mlir integer type. IIUC, it's an implementation value and it's not really representing a C/C++ int type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense. I'll look into trying that in a follow-up PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bcardosolopes I started looking at this, and the problem is that during LoweringPrepare, we extract this value from the attribute and pass it to a call to __dynamic_cast
. I think it makes sense to leave it as a #cir.int
.
This adds support for dynamic cast handling and generating `cir.dyn_cast` operations and `cir.dyn_cast_info` attributes. This does not include support for lowering the dynamic cast to LLVM IR, which will require changes to the LoweringPrepare pass that will be made in a future change. This also does not yet handle dynamic cast to void or exact dynamic casts.
This adds support for dynamic cast handling and generating
cir.dyn_cast
operations andcir.dyn_cast_info
attributes.This does not include support for lowering the dynamic cast to LLVM IR, which will require changes to the LoweringPrepare pass that will be made in a future change.
This also does not yet handle dynamic cast to void or exact dynamic casts.