Skip to content

Commit 56546fb

Browse files
committed
[CIR] Add initial support for dynamic cast
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.
1 parent f015c7f commit 56546fb

File tree

12 files changed

+315
-3
lines changed

12 files changed

+315
-3
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,12 @@ struct MissingFeatures {
8181
static bool opFuncExtraAttrs() { return false; }
8282
static bool opFuncMaybeHandleStaticInExternC() { return false; }
8383
static bool opFuncMultipleReturnVals() { return false; }
84+
static bool opFuncNoUnwind() { return false; }
8485
static bool opFuncOperandBundles() { return false; }
8586
static bool opFuncParameterAttributes() { return false; }
87+
static bool opFuncReadOnly() { return false; }
8688
static bool opFuncSection() { return false; }
89+
static bool opFuncWillReturn() { return false; }
8790
static bool setLLVMFunctionFEnvAttributes() { return false; }
8891
static bool setFunctionAttributes() { return false; }
8992

clang/lib/CIR/CodeGen/CIRGenBuilder.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
122122
return getPointerTo(cir::VPtrType::get(getContext()));
123123
}
124124

125+
cir::FuncType getFuncType(llvm::ArrayRef<mlir::Type> params, mlir::Type retTy,
126+
bool isVarArg = false) {
127+
return cir::FuncType::get(params, retTy, isVarArg);
128+
}
129+
125130
/// Get a CIR record kind from a AST declaration tag.
126131
cir::RecordType::RecordKind getRecordKind(const clang::TagTypeKind kind) {
127132
switch (kind) {
@@ -372,6 +377,15 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
372377
return cir::BinOp::create(*this, loc, cir::BinOpKind::Div, lhs, rhs);
373378
}
374379

380+
mlir::Value createDynCast(mlir::Location loc, mlir::Value src,
381+
cir::PointerType destType, bool isRefCast,
382+
cir::DynamicCastInfoAttr info) {
383+
auto castKind =
384+
isRefCast ? cir::DynamicCastKind::Ref : cir::DynamicCastKind::Ptr;
385+
return cir::DynamicCastOp::create(*this, loc, destType, castKind, src, info,
386+
/*relative_layout=*/false);
387+
}
388+
375389
Address createBaseClassAddr(mlir::Location loc, Address addr,
376390
mlir::Type destType, unsigned offset,
377391
bool assumeNotNull) {

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ class CIRGenCXXABI {
5454
Address thisAddr, const CXXRecordDecl *classDecl,
5555
const CXXRecordDecl *baseClassDecl) = 0;
5656

57+
virtual mlir::Value emitDynamicCast(CIRGenFunction &cgf, mlir::Location loc,
58+
QualType srcRecordTy,
59+
QualType destRecordTy,
60+
cir::PointerType destCIRTy,
61+
bool isRefCast, Address src) = 0;
62+
5763
public:
5864
/// Similar to AddedStructorArgs, but only notes the number of additional
5965
/// arguments.

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1185,10 +1185,16 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) {
11851185
case CK_BuiltinFnToFnPtr:
11861186
llvm_unreachable("builtin functions are handled elsewhere");
11871187

1188+
case CK_Dynamic: {
1189+
LValue lv = emitLValue(e->getSubExpr());
1190+
Address v = lv.getAddress();
1191+
const auto *dce = cast<CXXDynamicCastExpr>(e);
1192+
return makeNaturalAlignAddrLValue(emitDynamicCast(v, dce), e->getType());
1193+
}
1194+
11881195
// These are never l-values; just use the aggregate emission code.
11891196
case CK_NonAtomicToAtomic:
11901197
case CK_AtomicToNonAtomic:
1191-
case CK_Dynamic:
11921198
case CK_ToUnion:
11931199
case CK_BaseToDerived:
11941200
case CK_AddressSpaceConversion:

clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,3 +728,43 @@ void CIRGenFunction::emitDeleteCall(const FunctionDecl *deleteFD,
728728
// Emit the call to delete.
729729
emitNewDeleteCall(*this, deleteFD, deleteFTy, deleteArgs);
730730
}
731+
732+
mlir::Value CIRGenFunction::emitDynamicCast(Address thisAddr,
733+
const CXXDynamicCastExpr *dce) {
734+
mlir::Location loc = getLoc(dce->getSourceRange());
735+
736+
cgm.emitExplicitCastExprType(dce, this);
737+
QualType destTy = dce->getTypeAsWritten();
738+
QualType srcTy = dce->getSubExpr()->getType();
739+
740+
// C++ [expr.dynamic.cast]p7:
741+
// If T is "pointer to cv void," then the result is a pointer to the most
742+
// derived object pointed to by v.
743+
bool isDynCastToVoid = destTy->isVoidPointerType();
744+
bool isRefCast = destTy->isReferenceType();
745+
746+
QualType srcRecordTy;
747+
QualType destRecordTy;
748+
if (isDynCastToVoid) {
749+
srcRecordTy = srcTy->getPointeeType();
750+
// No destRecordTy.
751+
} else if (const PointerType *destPTy = destTy->getAs<PointerType>()) {
752+
srcRecordTy = srcTy->castAs<PointerType>()->getPointeeType();
753+
destRecordTy = destPTy->getPointeeType();
754+
} else {
755+
srcRecordTy = srcTy;
756+
destRecordTy = destTy->castAs<ReferenceType>()->getPointeeType();
757+
}
758+
759+
assert(srcRecordTy->isRecordType() && "source type must be a record type!");
760+
assert(!cir::MissingFeatures::emitTypeCheck());
761+
762+
if (dce->isAlwaysNull()) {
763+
cgm.errorNYI(dce->getSourceRange(), "emitDynamicCastToNull");
764+
return {};
765+
}
766+
767+
auto destCirTy = mlir::cast<cir::PointerType>(convertType(destTy));
768+
return cgm.getCXXABI().emitDynamicCast(*this, loc, srcRecordTy, destRecordTy,
769+
destCirTy, isRefCast, thisAddr);
770+
}

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1894,6 +1894,11 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) {
18941894
return v;
18951895
}
18961896

1897+
case CK_Dynamic: {
1898+
Address v = cgf.emitPointerWithAlignment(subExpr);
1899+
const auto *dce = cast<CXXDynamicCastExpr>(ce);
1900+
return cgf.emitDynamicCast(v, dce);
1901+
}
18971902
case CK_ArrayToPointerDecay:
18981903
return cgf.emitArrayToPointerDecay(subExpr).getPointer();
18991904

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,6 +1285,8 @@ class CIRGenFunction : public CIRGenTypeCache {
12851285

12861286
mlir::LogicalResult emitDoStmt(const clang::DoStmt &s);
12871287

1288+
mlir::Value emitDynamicCast(Address thisAddr, const CXXDynamicCastExpr *dce);
1289+
12881290
/// Emit an expression as an initializer for an object (variable, field, etc.)
12891291
/// at the given location. The expression is not necessarily the normal
12901292
/// initializer for the object, and the address is not necessarily

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,16 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
116116
Address thisAddr, const CXXRecordDecl *classDecl,
117117
const CXXRecordDecl *baseClassDecl) override;
118118

119+
// The traditional clang CodeGen emits calls to `__dynamic_cast` directly into
120+
// LLVM in the `emitDynamicCastCall` function. In CIR, `dynamic_cast`
121+
// expressions are lowered to `cir.dyn_cast` ops instead of calls to runtime
122+
// functions. So during CIRGen we don't need the `emitDynamicCastCall`
123+
// function that clang CodeGen has.
124+
mlir::Value emitDynamicCast(CIRGenFunction &cgf, mlir::Location loc,
125+
QualType srcRecordTy, QualType destRecordTy,
126+
cir::PointerType destCIRTy, bool isRefCast,
127+
Address src) override;
128+
119129
/**************************** RTTI Uniqueness ******************************/
120130
protected:
121131
/// Returns true if the ABI requires RTTI type_info objects to be unique
@@ -1796,3 +1806,143 @@ mlir::Value CIRGenItaniumCXXABI::getVirtualBaseClassOffset(
17961806
}
17971807
return vbaseOffset;
17981808
}
1809+
1810+
static cir::FuncOp getBadCastFn(CIRGenFunction &cgf) {
1811+
// Prototype: void __cxa_bad_cast();
1812+
1813+
// TODO(cir): set the calling convention of the runtime function.
1814+
assert(!cir::MissingFeatures::opFuncCallingConv());
1815+
1816+
cir::FuncType fnTy =
1817+
cgf.getBuilder().getFuncType({}, cgf.getBuilder().getVoidTy());
1818+
return cgf.cgm.createRuntimeFunction(fnTy, "__cxa_bad_cast");
1819+
}
1820+
1821+
// TODO(cir): This could be shared with classic codegen.
1822+
static CharUnits computeOffsetHint(ASTContext &astContext,
1823+
const CXXRecordDecl *src,
1824+
const CXXRecordDecl *dst) {
1825+
CXXBasePaths paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
1826+
/*DetectVirtual=*/false);
1827+
1828+
// If Dst is not derived from Src we can skip the whole computation below and
1829+
// return that Src is not a public base of Dst. Record all inheritance paths.
1830+
if (!dst->isDerivedFrom(src, paths))
1831+
return CharUnits::fromQuantity(-2ULL);
1832+
1833+
unsigned numPublicPaths = 0;
1834+
CharUnits offset;
1835+
1836+
// Now walk all possible inheritance paths.
1837+
for (const CXXBasePath &path : paths) {
1838+
if (path.Access != AS_public) // Ignore non-public inheritance.
1839+
continue;
1840+
1841+
++numPublicPaths;
1842+
1843+
for (const CXXBasePathElement &pathElement : path) {
1844+
// If the path contains a virtual base class we can't give any hint.
1845+
// -1: no hint.
1846+
if (pathElement.Base->isVirtual())
1847+
return CharUnits::fromQuantity(-1ULL);
1848+
1849+
if (numPublicPaths > 1) // Won't use offsets, skip computation.
1850+
continue;
1851+
1852+
// Accumulate the base class offsets.
1853+
const ASTRecordLayout &L =
1854+
astContext.getASTRecordLayout(pathElement.Class);
1855+
offset += L.getBaseClassOffset(
1856+
pathElement.Base->getType()->getAsCXXRecordDecl());
1857+
}
1858+
}
1859+
1860+
// -2: Src is not a public base of Dst.
1861+
if (numPublicPaths == 0)
1862+
return CharUnits::fromQuantity(-2ULL);
1863+
1864+
// -3: Src is a multiple public base type but never a virtual base type.
1865+
if (numPublicPaths > 1)
1866+
return CharUnits::fromQuantity(-3ULL);
1867+
1868+
// Otherwise, the Src type is a unique public nonvirtual base type of Dst.
1869+
// Return the offset of Src from the origin of Dst.
1870+
return offset;
1871+
}
1872+
1873+
static cir::FuncOp getItaniumDynamicCastFn(CIRGenFunction &cgf) {
1874+
// Prototype:
1875+
// void *__dynamic_cast(const void *sub,
1876+
// global_as const abi::__class_type_info *src,
1877+
// global_as const abi::__class_type_info *dst,
1878+
// std::ptrdiff_t src2dst_offset);
1879+
1880+
mlir::Type voidPtrTy = cgf.getBuilder().getVoidPtrTy();
1881+
mlir::Type rttiPtrTy = cgf.getBuilder().getUInt8PtrTy();
1882+
mlir::Type ptrDiffTy = cgf.convertType(cgf.getContext().getPointerDiffType());
1883+
1884+
// TODO(cir): mark the function as nowind willreturn readonly.
1885+
assert(!cir::MissingFeatures::opFuncNoUnwind());
1886+
assert(!cir::MissingFeatures::opFuncWillReturn());
1887+
assert(!cir::MissingFeatures::opFuncReadOnly());
1888+
1889+
// TODO(cir): set the calling convention of the runtime function.
1890+
assert(!cir::MissingFeatures::opFuncCallingConv());
1891+
1892+
cir::FuncType FTy = cgf.getBuilder().getFuncType(
1893+
{voidPtrTy, rttiPtrTy, rttiPtrTy, ptrDiffTy}, voidPtrTy);
1894+
return cgf.cgm.createRuntimeFunction(FTy, "__dynamic_cast");
1895+
}
1896+
1897+
static cir::DynamicCastInfoAttr emitDynamicCastInfo(CIRGenFunction &cgf,
1898+
mlir::Location loc,
1899+
QualType srcRecordTy,
1900+
QualType destRecordTy) {
1901+
auto srcRtti = mlir::cast<cir::GlobalViewAttr>(
1902+
cgf.cgm.getAddrOfRTTIDescriptor(loc, srcRecordTy));
1903+
auto destRtti = mlir::cast<cir::GlobalViewAttr>(
1904+
cgf.cgm.getAddrOfRTTIDescriptor(loc, destRecordTy));
1905+
1906+
cir::FuncOp runtimeFuncOp = getItaniumDynamicCastFn(cgf);
1907+
cir::FuncOp badCastFuncOp = getBadCastFn(cgf);
1908+
auto runtimeFuncRef = mlir::FlatSymbolRefAttr::get(runtimeFuncOp);
1909+
auto badCastFuncRef = mlir::FlatSymbolRefAttr::get(badCastFuncOp);
1910+
1911+
const CXXRecordDecl *srcDecl = srcRecordTy->getAsCXXRecordDecl();
1912+
const CXXRecordDecl *destDecl = destRecordTy->getAsCXXRecordDecl();
1913+
CharUnits offsetHint = computeOffsetHint(cgf.getContext(), srcDecl, destDecl);
1914+
1915+
mlir::Type ptrdiffTy = cgf.convertType(cgf.getContext().getPointerDiffType());
1916+
auto offsetHintAttr = cir::IntAttr::get(ptrdiffTy, offsetHint.getQuantity());
1917+
1918+
return cir::DynamicCastInfoAttr::get(srcRtti, destRtti, runtimeFuncRef,
1919+
badCastFuncRef, offsetHintAttr);
1920+
}
1921+
1922+
mlir::Value CIRGenItaniumCXXABI::emitDynamicCast(CIRGenFunction &cgf,
1923+
mlir::Location loc,
1924+
QualType srcRecordTy,
1925+
QualType destRecordTy,
1926+
cir::PointerType destCIRTy,
1927+
bool isRefCast, Address src) {
1928+
bool isCastToVoid = destRecordTy.isNull();
1929+
assert((!isCastToVoid || !isRefCast) && "cannot cast to void reference");
1930+
1931+
if (isCastToVoid) {
1932+
cgm.errorNYI(loc, "emitDynamicCastToVoid");
1933+
return {};
1934+
}
1935+
1936+
// If the destination is effectively final, the cast succeeds if and only
1937+
// if the dynamic type of the pointer is exactly the destination type.
1938+
if (destRecordTy->getAsCXXRecordDecl()->isEffectivelyFinal() &&
1939+
cgf.cgm.getCodeGenOpts().OptimizationLevel > 0) {
1940+
cgm.errorNYI(loc, "emitExactDynamicCast");
1941+
return {};
1942+
}
1943+
1944+
cir::DynamicCastInfoAttr castInfo =
1945+
emitDynamicCastInfo(cgf, loc, srcRecordTy, destRecordTy);
1946+
return cgf.getBuilder().createDynCast(loc, src.getPointer(), destCIRTy,
1947+
isRefCast, castInfo);
1948+
}

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2079,6 +2079,29 @@ CIRGenModule::createCIRBuiltinFunction(mlir::Location loc, StringRef name,
20792079
return fnOp;
20802080
}
20812081

2082+
cir::FuncOp CIRGenModule::createRuntimeFunction(cir::FuncType ty,
2083+
StringRef name, mlir::ArrayAttr,
2084+
[[maybe_unused]] bool isLocal,
2085+
bool assumeConvergent) {
2086+
if (assumeConvergent)
2087+
errorNYI("createRuntimeFunction: assumeConvergent");
2088+
if (isLocal)
2089+
errorNYI("createRuntimeFunction: local");
2090+
2091+
cir::FuncOp entry = getOrCreateCIRFunction(name, ty, GlobalDecl(),
2092+
/*forVtable=*/false);
2093+
2094+
if (entry) {
2095+
// TODO(cir): set the attributes of the function.
2096+
assert(!cir::MissingFeatures::setLLVMFunctionFEnvAttributes());
2097+
assert(!cir::MissingFeatures::opFuncCallingConv());
2098+
assert(!cir::MissingFeatures::opGlobalDLLImportExport());
2099+
entry.setDSOLocal(true);
2100+
}
2101+
2102+
return entry;
2103+
}
2104+
20822105
mlir::SymbolTable::Visibility
20832106
CIRGenModule::getMLIRVisibility(cir::GlobalOp op) {
20842107
// MLIR doesn't accept public symbols declarations (only

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,10 @@ class CIRGenModule : public CIRGenTypeCache {
480480
cir::FuncType ty,
481481
const clang::FunctionDecl *fd);
482482

483+
cir::FuncOp createRuntimeFunction(cir::FuncType ty, llvm::StringRef name,
484+
mlir::ArrayAttr = {}, bool isLocal = false,
485+
bool assumeConvergent = false);
486+
483487
static constexpr const char *builtinCoroId = "__builtin_coro_id";
484488

485489
/// Given a builtin id for a function like "__builtin_fabsf", return a

0 commit comments

Comments
 (0)