Index: llvm/include/llvm/Analysis/TypeMetadataUtils.h =================================================================== --- llvm/include/llvm/Analysis/TypeMetadataUtils.h +++ llvm/include/llvm/Analysis/TypeMetadataUtils.h @@ -22,6 +22,7 @@ class CallBase; class CallInst; class Constant; +class Function; class DominatorTree; class Instruction; class Module; @@ -56,25 +57,30 @@ SmallVectorImpl &Preds, bool &HasNonCallUses, const CallInst *CI, DominatorTree &DT); -// Processes a Constant recursively looking into elements of arrays, structs and -// expressions to find a trivial pointer element that is located at the given -// offset (relative to the beginning of the whole outer Constant). -// -// Used for example from GlobalDCE to find an entry in a C++ vtable that matches -// a vcall offset. -// -// To support Swift vtables, getPointerAtOffset can see through "relative -// pointers", i.e. (sub-)expressions of the form of: -// -// @symbol = ... { -// i32 trunc (i64 sub ( -// i64 ptrtoint ( @target to i64), i64 ptrtoint (... @symbol to i64) -// ) to i32) -// } -// -// For such (sub-)expressions, getPointerAtOffset returns the @target pointer. +/// Processes a Constant recursively looking into elements of arrays, structs +/// and expressions to find a trivial pointer element that is located at the +/// given offset (relative to the beginning of the whole outer Constant). +/// +/// Used for example from GlobalDCE to find an entry in a C++ vtable that +/// matches a vcall offset. +/// +/// To support Swift vtables, getPointerAtOffset can see through "relative +/// pointers", i.e. (sub-)expressions of the form of: +/// +/// @symbol = ... { +/// i32 trunc (i64 sub ( +/// i64 ptrtoint ( @target to i64), i64 ptrtoint (... @symbol to i64) +/// ) to i32) +/// } +/// +/// For such (sub-)expressions, getPointerAtOffset returns the @target pointer. Constant *getPointerAtOffset(Constant *I, uint64_t Offset, Module &M, Constant *TopLevelGlobal = nullptr); -} + +/// Finds the same "relative pointer" pattern as described above, where the +/// target is `F`, and replaces the entire pattern with a constant zero. +void replaceRelativePointerUsersWithZero(Function *F); + +} // namespace llvm #endif Index: llvm/lib/Analysis/TypeMetadataUtils.cpp =================================================================== --- llvm/lib/Analysis/TypeMetadataUtils.cpp +++ llvm/lib/Analysis/TypeMetadataUtils.cpp @@ -197,3 +197,24 @@ } return nullptr; } + +void llvm::replaceRelativePointerUsersWithZero(Function *F) { + for (auto *U : F->users()) { + auto *PtrExpr = dyn_cast(U); + if (!PtrExpr) + continue; + if (PtrExpr->getOpcode() != Instruction::PtrToInt) + continue; + + for (auto *PtrToIntUser : PtrExpr->users()) { + auto *SubExpr = dyn_cast(PtrExpr->user_back()); + if (!SubExpr) + continue; + if (SubExpr->getOpcode() != Instruction::Sub) + continue; + + SubExpr->replaceNonMetadataUsesWith( + ConstantInt::get(SubExpr->getType(), 0)); + } + } +} Index: llvm/lib/Transforms/IPO/GlobalDCE.cpp =================================================================== --- llvm/lib/Transforms/IPO/GlobalDCE.cpp +++ llvm/lib/Transforms/IPO/GlobalDCE.cpp @@ -416,6 +416,16 @@ // virtual function pointers with null, allowing us to remove the // function itself. ++NumVFuncs; + + // Detect vfuncs that are referenced as "relative pointers" which are used + // in Swift vtables, i.e. entries in the form of: + // + // i32 trunc (i64 sub (i64 ptrtoint @f, i64 ptrtoint ...)) to i32) + // + // In this case, replace the whole "sub" expression with constant 0 to + // avoid leaving a weird sub(0, symbol) expression behind. + replaceRelativePointerUsersWithZero(F); + F->replaceNonMetadataUsesWith(ConstantPointerNull::get(F->getType())); } EraseUnusedGlobalValue(F); Index: llvm/test/Transforms/GlobalDCE/virtual-functions-relative-pointers-bad.ll =================================================================== --- llvm/test/Transforms/GlobalDCE/virtual-functions-relative-pointers-bad.ll +++ llvm/test/Transforms/GlobalDCE/virtual-functions-relative-pointers-bad.ll @@ -14,11 +14,7 @@ !0 = !{i64 0, !"vfunc1.type"} !1 = !{i64 4, !"vfunc2.type"} -; CHECK: @vtable = internal unnamed_addr constant { [3 x i32] } { [3 x i32] [ -; CHECK-SAME: i32 trunc (i64 sub (i64 0, i64 ptrtoint ({ [3 x i32] }* @vtable to i64)) to i32), -; CHECK-SAME: i32 trunc (i64 sub (i64 0, i64 ptrtoint ({ [3 x i32] }* @vtable to i64)) to i32), -; CHECK-SAME: i32 trunc (i64 sub (i64 0, i64 ptrtoint (void ()* @weird_ref_2 to i64)) to i32) -; CHECK-SAME: ] }, align 8, !type !0, !type !1, !vcall_visibility !2 +; CHECK: @vtable = internal unnamed_addr constant { [3 x i32] } zeroinitializer, align 8, !type !0, !type !1, !vcall_visibility !2 define internal void @vfunc1() { ret void } define internal void @vfunc2() { ret void } Index: llvm/test/Transforms/GlobalDCE/virtual-functions-relative-pointers-gep.ll =================================================================== --- llvm/test/Transforms/GlobalDCE/virtual-functions-relative-pointers-gep.ll +++ llvm/test/Transforms/GlobalDCE/virtual-functions-relative-pointers-gep.ll @@ -16,7 +16,7 @@ ; CHECK: @vtable = internal unnamed_addr constant { [4 x i32] } { [4 x i32] [ ; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint (void ()* @vfunc1_live to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [4 x i32] }, { [4 x i32] }* @vtable, i32 0, i32 0, i32 2) to i64)) to i32), -; CHECK-SAME: i32 trunc (i64 sub (i64 0, i64 ptrtoint (i32* getelementptr inbounds ({ [4 x i32] }, { [4 x i32] }* @vtable, i32 0, i32 0, i32 2) to i64)) to i32) +; CHECK-SAME: i32 0 ; CHECK-SAME: ] }, align 8, !type !0, !type !1, !vcall_visibility !2 ; (1) vfunc1_live is referenced from @main, stays alive Index: llvm/test/Transforms/GlobalDCE/virtual-functions-relative-pointers.ll =================================================================== --- llvm/test/Transforms/GlobalDCE/virtual-functions-relative-pointers.ll +++ llvm/test/Transforms/GlobalDCE/virtual-functions-relative-pointers.ll @@ -14,7 +14,7 @@ ; CHECK: @vtable = internal unnamed_addr constant { [2 x i32] } { [2 x i32] [ ; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint (void ()* @vfunc1_live to i64), i64 ptrtoint ({ [2 x i32] }* @vtable to i64)) to i32), -; CHECK-SAME: i32 trunc (i64 sub (i64 0, i64 ptrtoint ({ [2 x i32] }* @vtable to i64)) to i32) +; CHECK-SAME: i32 0 ; CHECK-SAME: ] }, align 8, !type !0, !type !1, !vcall_visibility !2 ; (1) vfunc1_live is referenced from @main, stays alive