@@ -1869,6 +1869,15 @@ static cir::FuncOp getBadCastFn(CIRGenFunction &cgf) {
18691869 return cgf.cgm .createRuntimeFunction (fnTy, " __cxa_bad_cast" );
18701870}
18711871
1872+ static void emitCallToBadCast (CIRGenFunction &cgf, mlir::Location loc) {
1873+ // TODO(cir): set the calling convention to the runtime function.
1874+ assert (!cir::MissingFeatures::opFuncCallingConv ());
1875+
1876+ cgf.emitRuntimeCall (loc, getBadCastFn (cgf));
1877+ cir::UnreachableOp::create (cgf.getBuilder (), loc);
1878+ cgf.getBuilder ().clearInsertionPoint ();
1879+ }
1880+
18721881// TODO(cir): This could be shared with classic codegen.
18731882static CharUnits computeOffsetHint (ASTContext &astContext,
18741883 const CXXRecordDecl *src,
@@ -1954,6 +1963,136 @@ static Address emitDynamicCastToVoid(CIRGenFunction &cgf, mlir::Location loc,
19541963 return Address{ptr, src.getAlignment ()};
19551964}
19561965
1966+ static mlir::Value emitExactDynamicCast (CIRGenItaniumCXXABI &abi,
1967+ CIRGenFunction &cgf, mlir::Location loc,
1968+ QualType srcRecordTy,
1969+ QualType destRecordTy,
1970+ cir::PointerType destCIRTy,
1971+ bool isRefCast, Address src) {
1972+ // Find all the inheritance paths from SrcRecordTy to DestRecordTy.
1973+ const CXXRecordDecl *srcDecl = srcRecordTy->getAsCXXRecordDecl ();
1974+ const CXXRecordDecl *destDecl = destRecordTy->getAsCXXRecordDecl ();
1975+ CXXBasePaths paths (/* FindAmbiguities=*/ true , /* RecordPaths=*/ true ,
1976+ /* DetectVirtual=*/ false );
1977+ (void )destDecl->isDerivedFrom (srcDecl, paths);
1978+
1979+ // Find an offset within `destDecl` where a `srcDecl` instance and its vptr
1980+ // might appear.
1981+ std::optional<CharUnits> offset;
1982+ for (const CXXBasePath &path : paths) {
1983+ // dynamic_cast only finds public inheritance paths.
1984+ if (path.Access != AS_public)
1985+ continue ;
1986+
1987+ CharUnits pathOffset;
1988+ for (const CXXBasePathElement &pathElement : path) {
1989+ // Find the offset along this inheritance step.
1990+ const CXXRecordDecl *base =
1991+ pathElement.Base ->getType ()->getAsCXXRecordDecl ();
1992+ if (pathElement.Base ->isVirtual ()) {
1993+ // For a virtual base class, we know that the derived class is exactly
1994+ // destDecl, so we can use the vbase offset from its layout.
1995+ const ASTRecordLayout &layout =
1996+ cgf.getContext ().getASTRecordLayout (destDecl);
1997+ pathOffset = layout.getVBaseClassOffset (base);
1998+ } else {
1999+ const ASTRecordLayout &layout =
2000+ cgf.getContext ().getASTRecordLayout (pathElement.Class );
2001+ pathOffset += layout.getBaseClassOffset (base);
2002+ }
2003+ }
2004+
2005+ if (!offset) {
2006+ offset = pathOffset;
2007+ } else if (offset != pathOffset) {
2008+ // base appears in at least two different places. Find the most-derived
2009+ // object and see if it's a DestDecl. Note that the most-derived object
2010+ // must be at least as aligned as this base class subobject, and must
2011+ // have a vptr at offset 0.
2012+ src = emitDynamicCastToVoid (cgf, loc, srcRecordTy, src);
2013+ srcDecl = destDecl;
2014+ offset = CharUnits::Zero ();
2015+ break ;
2016+ }
2017+ }
2018+
2019+ CIRGenBuilderTy &builder = cgf.getBuilder ();
2020+
2021+ if (!offset) {
2022+ // If there are no public inheritance paths, the cast always fails.
2023+ mlir::Value nullPtrValue = builder.getNullPtr (destCIRTy, loc);
2024+ if (isRefCast) {
2025+ mlir::Region *currentRegion = builder.getBlock ()->getParent ();
2026+ emitCallToBadCast (cgf, loc);
2027+
2028+ // The call to bad_cast will terminate the block. Create a new block to
2029+ // hold any follow up code.
2030+ builder.createBlock (currentRegion, currentRegion->end ());
2031+ }
2032+
2033+ return nullPtrValue;
2034+ }
2035+
2036+ // Compare the vptr against the expected vptr for the destination type at
2037+ // this offset. Note that we do not know what type src points to in the case
2038+ // where the derived class multiply inherits from the base class so we can't
2039+ // use getVTablePtr, so we load the vptr directly instead.
2040+
2041+ mlir::Value expectedVPtr =
2042+ abi.getVTableAddressPoint (BaseSubobject (srcDecl, *offset), destDecl);
2043+
2044+ // TODO(cir): handle address space here.
2045+ assert (!cir::MissingFeatures::addressSpace ());
2046+ mlir::Type vptrTy = expectedVPtr.getType ();
2047+ mlir::Type vptrPtrTy = builder.getPointerTo (vptrTy);
2048+ Address srcVPtrPtr (builder.createBitcast (src.getPointer (), vptrPtrTy),
2049+ src.getAlignment ());
2050+ mlir::Value srcVPtr = builder.createLoad (loc, srcVPtrPtr);
2051+
2052+ // TODO(cir): decorate SrcVPtr with TBAA info.
2053+ assert (!cir::MissingFeatures::opTBAA ());
2054+
2055+ mlir::Value success =
2056+ builder.createCompare (loc, cir::CmpOpKind::eq, srcVPtr, expectedVPtr);
2057+
2058+ auto emitCastResult = [&] {
2059+ if (offset->isZero ())
2060+ return builder.createBitcast (src.getPointer (), destCIRTy);
2061+
2062+ // TODO(cir): handle address space here.
2063+ assert (!cir::MissingFeatures::addressSpace ());
2064+ mlir::Type u8PtrTy = builder.getUInt8PtrTy ();
2065+
2066+ mlir::Value strideToApply =
2067+ builder.getConstInt (loc, builder.getUInt64Ty (), offset->getQuantity ());
2068+ mlir::Value srcU8Ptr = builder.createBitcast (src.getPointer (), u8PtrTy);
2069+ mlir::Value resultU8Ptr = cir::PtrStrideOp::create (builder, loc, u8PtrTy,
2070+ srcU8Ptr, strideToApply);
2071+ return builder.createBitcast (resultU8Ptr, destCIRTy);
2072+ };
2073+
2074+ if (isRefCast) {
2075+ mlir::Value failed = builder.createNot (success);
2076+ cir::IfOp::create (builder, loc, failed, /* withElseRegion=*/ false ,
2077+ [&](mlir::OpBuilder &, mlir::Location) {
2078+ emitCallToBadCast (cgf, loc);
2079+ });
2080+ return emitCastResult ();
2081+ }
2082+
2083+ return cir::TernaryOp::create (
2084+ builder, loc, success,
2085+ [&](mlir::OpBuilder &, mlir::Location) {
2086+ auto result = emitCastResult ();
2087+ builder.createYield (loc, result);
2088+ },
2089+ [&](mlir::OpBuilder &, mlir::Location) {
2090+ mlir::Value nullPtrValue = builder.getNullPtr (destCIRTy, loc);
2091+ builder.createYield (loc, nullPtrValue);
2092+ })
2093+ .getResult ();
2094+ }
2095+
19572096static cir::DynamicCastInfoAttr emitDynamicCastInfo (CIRGenFunction &cgf,
19582097 mlir::Location loc,
19592098 QualType srcRecordTy,
@@ -1995,8 +2134,27 @@ mlir::Value CIRGenItaniumCXXABI::emitDynamicCast(CIRGenFunction &cgf,
19952134 // if the dynamic type of the pointer is exactly the destination type.
19962135 if (destRecordTy->getAsCXXRecordDecl ()->isEffectivelyFinal () &&
19972136 cgf.cgm .getCodeGenOpts ().OptimizationLevel > 0 ) {
1998- cgm.errorNYI (loc, " emitExactDynamicCast" );
1999- return {};
2137+ CIRGenBuilderTy &builder = cgf.getBuilder ();
2138+ // If this isn't a reference cast, check the pointer to see if it's null.
2139+ if (!isRefCast) {
2140+ mlir::Value srcPtrIsNull = builder.createPtrIsNull (src.getPointer ());
2141+ return cir::TernaryOp::create (
2142+ builder, loc, srcPtrIsNull,
2143+ [&](mlir::OpBuilder, mlir::Location) {
2144+ builder.createYield (
2145+ loc, builder.getNullPtr (destCIRTy, loc).getResult ());
2146+ },
2147+ [&](mlir::OpBuilder &, mlir::Location) {
2148+ mlir::Value exactCast = emitExactDynamicCast (
2149+ *this , cgf, loc, srcRecordTy, destRecordTy, destCIRTy,
2150+ isRefCast, src);
2151+ builder.createYield (loc, exactCast);
2152+ })
2153+ .getResult ();
2154+ }
2155+
2156+ return emitExactDynamicCast (*this , cgf, loc, srcRecordTy, destRecordTy,
2157+ destCIRTy, isRefCast, src);
20002158 }
20012159
20022160 cir::DynamicCastInfoAttr castInfo =
0 commit comments