Index: llvm/lib/Transforms/IPO/GlobalDCE.cpp =================================================================== --- llvm/lib/Transforms/IPO/GlobalDCE.cpp +++ llvm/lib/Transforms/IPO/GlobalDCE.cpp @@ -416,6 +416,29 @@ // 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. + for (auto *U : F->users()) { + if (auto *PtrExpr = dyn_cast(U)) { + if (PtrExpr->getOpcode() == Instruction::PtrToInt) { + assert(PtrExpr->hasOneUser()); + if (auto *SubExpr = dyn_cast(PtrExpr->user_back())) { + assert(SubExpr->hasOneUser()); + if (SubExpr->getOpcode() == Instruction::Sub) { + SubExpr->replaceNonMetadataUsesWith( + ConstantInt::get(SubExpr->getType(), 0)); + } + } + } + } + } + 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 @@ -15,9 +15,9 @@ !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: i32 0, +; CHECK-SAME: i32 0, +; CHECK-SAME: i32 0 ; CHECK-SAME: ] }, align 8, !type !0, !type !1, !vcall_visibility !2 define internal void @vfunc1() { 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