Index: llvm/lib/Transforms/IPO/GlobalDCE.cpp =================================================================== --- llvm/lib/Transforms/IPO/GlobalDCE.cpp +++ llvm/lib/Transforms/IPO/GlobalDCE.cpp @@ -416,7 +416,43 @@ // virtual function pointers with null, allowing us to remove the // function itself. ++NumVFuncs; - F->replaceNonMetadataUsesWith(ConstantPointerNull::get(F->getType())); + + // 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 *CE = dyn_cast(U)) { + if (CE->getOpcode() == Instruction::PtrToInt) { + assert(CE->hasOneUser()); + if (auto *SubExpr = dyn_cast(CE->user_back())) { + assert(SubExpr->hasOneUser()); + if (SubExpr->getOpcode() == Instruction::Sub) { + SubExpr->replaceNonMetadataUsesWith( + ConstantInt::get(SubExpr->getType(), 0)); + + // Replacing the sub takes care of the entire user U, no need + // do U->replaceUsesOfWith. + continue; + } + } + } + + if (CE->getOpcode() == Instruction::BitCast) { + assert(CE->hasOneUser()); + CE->replaceNonMetadataUsesWith(ConstantPointerNull::get(F->getType())); + + // Replacing the bitcast takes care of the entire user U, no need + // do U->replaceUsesOfWith. + continue; + } + } + + U->replaceUsesOfWith(F, ConstantPointerNull::get(F->getType())); + } } EraseUnusedGlobalValue(F); } Index: llvm/test/Transforms/GlobalDCE/virtual-functions-derived-call.ll =================================================================== --- llvm/test/Transforms/GlobalDCE/virtual-functions-derived-call.ll +++ llvm/test/Transforms/GlobalDCE/virtual-functions-derived-call.ll @@ -25,7 +25,7 @@ %struct.A = type { i32 (...)** } %struct.B = type { %struct.A } -; CHECK: @_ZTV1A = internal unnamed_addr constant { [3 x i8*] } zeroinitializer +; CHECK: @_ZTV1A = internal unnamed_addr constant { [3 x i8*] } { [3 x i8*] [i8* null, i8* null, i8* null] } @_ZTV1A = internal unnamed_addr constant { [3 x i8*] } { [3 x i8*] [i8* null, i8* null, i8* bitcast (i32 (%struct.A*)* @_ZN1A3fooEv to i8*)] }, align 8, !type !0, !type !1, !vcall_visibility !2 ; CHECK: @_ZTV1B = internal unnamed_addr constant { [3 x i8*] } { [3 x i8*] [i8* null, i8* null, i8* bitcast (i32 (%struct.B*)* @_ZN1B3fooEv to i8*)] } Index: llvm/test/Transforms/GlobalDCE/virtual-functions-derived-pointer-call.ll =================================================================== --- llvm/test/Transforms/GlobalDCE/virtual-functions-derived-pointer-call.ll +++ llvm/test/Transforms/GlobalDCE/virtual-functions-derived-pointer-call.ll @@ -34,7 +34,7 @@ %struct.A = type { i32 (...)** } %struct.B = type { %struct.A } -; CHECK: @_ZTV1A = internal unnamed_addr constant { [4 x i8*] } zeroinitializer +; CHECK: @_ZTV1A = internal unnamed_addr constant { [4 x i8*] } { [4 x i8*] [i8* null, i8* null, i8* null, i8* null] } @_ZTV1A = internal unnamed_addr constant { [4 x i8*] } { [4 x i8*] [i8* null, i8* null, i8* bitcast (i32 (%struct.A*, i32)* @_ZN1A3fooEi to i8*), i8* bitcast (i32 (%struct.A*, float)* @_ZN1A3barEf to i8*)] }, align 8, !type !0, !type !1, !type !2, !vcall_visibility !3 ; CHECK: @_ZTV1B = internal unnamed_addr constant { [4 x i8*] } { [4 x i8*] [i8* null, i8* null, i8* bitcast (i32 (%struct.B*, i32)* @_ZN1B3fooEi to i8*), i8* null] } @_ZTV1B = internal unnamed_addr constant { [4 x i8*] } { [4 x i8*] [i8* null, i8* null, i8* bitcast (i32 (%struct.B*, i32)* @_ZN1B3fooEi to i8*), i8* bitcast (i32 (%struct.B*, float)* @_ZN1B3barEf to i8*)] }, align 8, !type !0, !type !1, !type !2, !type !4, !type !5, !type !6, !vcall_visibility !3 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