|
36 | 36 | #include "llvm/IR/Value.h" |
37 | 37 | #include "llvm/Support/ScopedPrinter.h" |
38 | 38 |
|
| 39 | +#include <optional> |
| 40 | + |
39 | 41 | using namespace clang; |
40 | 42 | using namespace CodeGen; |
41 | 43 |
|
@@ -185,11 +187,56 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI { |
185 | 187 | bool shouldDynamicCastCallBeNullChecked(bool SrcIsPtr, |
186 | 188 | QualType SrcRecordTy) override; |
187 | 189 |
|
| 190 | + /// Determine whether we know that all instances of type RecordTy will have |
| 191 | + /// the same vtable pointer values, that is distinct from all other vtable |
| 192 | + /// pointers. While this is required by the Itanium ABI, it doesn't happen in |
| 193 | + /// practice in some cases due to language extensions. |
| 194 | + bool hasUniqueVTablePointer(QualType RecordTy) { |
| 195 | + const CXXRecordDecl *RD = RecordTy->getAsCXXRecordDecl(); |
| 196 | + |
| 197 | + // Under -fapple-kext, multiple definitions of the same vtable may be |
| 198 | + // emitted. |
| 199 | + if (!CGM.getCodeGenOpts().AssumeUniqueVTables || |
| 200 | + getContext().getLangOpts().AppleKext) |
| 201 | + return false; |
| 202 | + |
| 203 | + // If the type_info* would be null, the vtable might be merged with that of |
| 204 | + // another type. |
| 205 | + if (!CGM.shouldEmitRTTI()) |
| 206 | + return false; |
| 207 | + |
| 208 | + // If there's only one definition of the vtable in the program, it has a |
| 209 | + // unique address. |
| 210 | + if (!llvm::GlobalValue::isWeakForLinker(CGM.getVTableLinkage(RD))) |
| 211 | + return true; |
| 212 | + |
| 213 | + // Even if there are multiple definitions of the vtable, they are required |
| 214 | + // by the ABI to use the same symbol name, so should be merged at load |
| 215 | + // time. However, if the class has hidden visibility, there can be |
| 216 | + // different versions of the class in different modules, and the ABI |
| 217 | + // library might treat them as being the same. |
| 218 | + if (CGM.GetLLVMVisibility(RD->getVisibility()) != |
| 219 | + llvm::GlobalValue::DefaultVisibility) |
| 220 | + return false; |
| 221 | + |
| 222 | + return true; |
| 223 | + } |
| 224 | + |
| 225 | + bool shouldEmitExactDynamicCast(QualType DestRecordTy) override { |
| 226 | + return hasUniqueVTablePointer(DestRecordTy); |
| 227 | + } |
| 228 | + |
188 | 229 | llvm::Value *emitDynamicCastCall(CodeGenFunction &CGF, Address Value, |
189 | 230 | QualType SrcRecordTy, QualType DestTy, |
190 | 231 | QualType DestRecordTy, |
191 | 232 | llvm::BasicBlock *CastEnd) override; |
192 | 233 |
|
| 234 | + llvm::Value *emitExactDynamicCast(CodeGenFunction &CGF, Address ThisAddr, |
| 235 | + QualType SrcRecordTy, QualType DestTy, |
| 236 | + QualType DestRecordTy, |
| 237 | + llvm::BasicBlock *CastSuccess, |
| 238 | + llvm::BasicBlock *CastFail) override; |
| 239 | + |
193 | 240 | llvm::Value *emitDynamicCastToVoid(CodeGenFunction &CGF, Address Value, |
194 | 241 | QualType SrcRecordTy) override; |
195 | 242 |
|
@@ -1202,7 +1249,8 @@ void ItaniumCXXABI::emitVirtualObjectDelete(CodeGenFunction &CGF, |
1202 | 1249 | // Track back to entry -2 and pull out the offset there. |
1203 | 1250 | llvm::Value *OffsetPtr = CGF.Builder.CreateConstInBoundsGEP1_64( |
1204 | 1251 | CGF.IntPtrTy, VTable, -2, "complete-offset.ptr"); |
1205 | | - llvm::Value *Offset = CGF.Builder.CreateAlignedLoad(CGF.IntPtrTy, OffsetPtr, CGF.getPointerAlign()); |
| 1252 | + llvm::Value *Offset = CGF.Builder.CreateAlignedLoad(CGF.IntPtrTy, OffsetPtr, |
| 1253 | + CGF.getPointerAlign()); |
1206 | 1254 |
|
1207 | 1255 | // Apply the offset. |
1208 | 1256 | llvm::Value *CompletePtr = |
@@ -1463,6 +1511,84 @@ llvm::Value *ItaniumCXXABI::emitDynamicCastCall( |
1463 | 1511 | return Value; |
1464 | 1512 | } |
1465 | 1513 |
|
| 1514 | +llvm::Value *ItaniumCXXABI::emitExactDynamicCast( |
| 1515 | + CodeGenFunction &CGF, Address ThisAddr, QualType SrcRecordTy, |
| 1516 | + QualType DestTy, QualType DestRecordTy, llvm::BasicBlock *CastSuccess, |
| 1517 | + llvm::BasicBlock *CastFail) { |
| 1518 | + ASTContext &Context = getContext(); |
| 1519 | + |
| 1520 | + // Find all the inheritance paths. |
| 1521 | + const CXXRecordDecl *SrcDecl = SrcRecordTy->getAsCXXRecordDecl(); |
| 1522 | + const CXXRecordDecl *DestDecl = DestRecordTy->getAsCXXRecordDecl(); |
| 1523 | + CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, |
| 1524 | + /*DetectVirtual=*/false); |
| 1525 | + (void)DestDecl->isDerivedFrom(SrcDecl, Paths); |
| 1526 | + |
| 1527 | + // Find an offset within `DestDecl` where a `SrcDecl` instance and its vptr |
| 1528 | + // might appear. |
| 1529 | + std::optional<CharUnits> Offset; |
| 1530 | + for (const CXXBasePath &Path : Paths) { |
| 1531 | + // dynamic_cast only finds public inheritance paths. |
| 1532 | + if (Path.Access != AS_public) |
| 1533 | + continue; |
| 1534 | + |
| 1535 | + CharUnits PathOffset; |
| 1536 | + for (const CXXBasePathElement &PathElement : Path) { |
| 1537 | + // Find the offset along this inheritance step. |
| 1538 | + const CXXRecordDecl *Base = |
| 1539 | + PathElement.Base->getType()->getAsCXXRecordDecl(); |
| 1540 | + if (PathElement.Base->isVirtual()) { |
| 1541 | + // For a virtual base class, we know that the derived class is exactly |
| 1542 | + // DestDecl, so we can use the vbase offset from its layout. |
| 1543 | + const ASTRecordLayout &L = Context.getASTRecordLayout(DestDecl); |
| 1544 | + PathOffset = L.getVBaseClassOffset(Base); |
| 1545 | + } else { |
| 1546 | + const ASTRecordLayout &L = |
| 1547 | + Context.getASTRecordLayout(PathElement.Class); |
| 1548 | + PathOffset += L.getBaseClassOffset(Base); |
| 1549 | + } |
| 1550 | + } |
| 1551 | + |
| 1552 | + if (!Offset) |
| 1553 | + Offset = PathOffset; |
| 1554 | + else if (Offset != PathOffset) { |
| 1555 | + // Base appears in at least two different places. Find the most-derived |
| 1556 | + // object and see if it's a DestDecl. Note that the most-derived object |
| 1557 | + // must be at least as aligned as this base class subobject, and must |
| 1558 | + // have a vptr at offset 0. |
| 1559 | + ThisAddr = Address(emitDynamicCastToVoid(CGF, ThisAddr, SrcRecordTy), |
| 1560 | + CGF.VoidPtrTy, ThisAddr.getAlignment()); |
| 1561 | + SrcDecl = DestDecl; |
| 1562 | + Offset = CharUnits::Zero(); |
| 1563 | + break; |
| 1564 | + } |
| 1565 | + } |
| 1566 | + |
| 1567 | + if (!Offset) { |
| 1568 | + // If there are no public inheritance paths, the cast always fails. |
| 1569 | + CGF.EmitBranch(CastFail); |
| 1570 | + return llvm::PoisonValue::get(CGF.VoidPtrTy); |
| 1571 | + } |
| 1572 | + |
| 1573 | + // Compare the vptr against the expected vptr for the destination type at |
| 1574 | + // this offset. Note that we do not know what type ThisAddr points to in |
| 1575 | + // the case where the derived class multiply inherits from the base class |
| 1576 | + // so we can't use GetVTablePtr, so we load the vptr directly instead. |
| 1577 | + llvm::Instruction *VPtr = CGF.Builder.CreateLoad( |
| 1578 | + ThisAddr.withElementType(CGF.VoidPtrPtrTy), "vtable"); |
| 1579 | + CGM.DecorateInstructionWithTBAA( |
| 1580 | + VPtr, CGM.getTBAAVTablePtrAccessInfo(CGF.VoidPtrPtrTy)); |
| 1581 | + llvm::Value *Success = CGF.Builder.CreateICmpEQ( |
| 1582 | + VPtr, getVTableAddressPoint(BaseSubobject(SrcDecl, *Offset), DestDecl)); |
| 1583 | + llvm::Value *Result = ThisAddr.getPointer(); |
| 1584 | + if (!Offset->isZero()) |
| 1585 | + Result = CGF.Builder.CreateInBoundsGEP( |
| 1586 | + CGF.CharTy, Result, |
| 1587 | + {llvm::ConstantInt::get(CGF.PtrDiffTy, -Offset->getQuantity())}); |
| 1588 | + CGF.Builder.CreateCondBr(Success, CastSuccess, CastFail); |
| 1589 | + return Result; |
| 1590 | +} |
| 1591 | + |
1466 | 1592 | llvm::Value *ItaniumCXXABI::emitDynamicCastToVoid(CodeGenFunction &CGF, |
1467 | 1593 | Address ThisAddr, |
1468 | 1594 | QualType SrcRecordTy) { |
|
0 commit comments