Index: llvm/lib/Transforms/IPO/GlobalDCE.cpp =================================================================== --- llvm/lib/Transforms/IPO/GlobalDCE.cpp +++ llvm/lib/Transforms/IPO/GlobalDCE.cpp @@ -145,6 +145,8 @@ return false; } +static const char kVFETrapFunctionName[] = "___globaldce_vfe_trap"; + void GlobalDCEPass::UpdateGVDependencies(GlobalValue &GV) { SmallPtrSet Deps; for (User *User : GV.users()) @@ -165,6 +167,10 @@ } } + if (IgnoreDependency && (GV.getName() == kVFETrapFunctionName)) { + IgnoreDependency = false; + } + if (IgnoreDependency) { LLVM_DEBUG(dbgs() << "Ignoring dep " << GVU->getName() << " -> " << GV.getName() << "\n"); @@ -321,6 +327,22 @@ ); } +static Value *getOrCreateTrapFunction(Module &M, FunctionType *T) { + if (!M.getFunction(kVFETrapFunctionName)) { + auto VoidTy = Type::getVoidTy(M.getContext()); + auto FnTy = FunctionType::get(VoidTy, /*varArg*/ false); + auto F = Function::Create(FnTy, GlobalValue::InternalLinkage, + kVFETrapFunctionName, &M); + auto *Entry = BasicBlock::Create(M.getContext(), "entry", F); + + Function *TrapIntrinsic = Intrinsic::getDeclaration(&M, Intrinsic::trap); + CallInst::Create(TrapIntrinsic, "", Entry); + new UnreachableInst(M.getContext(), Entry); + } + + return M.getOrInsertFunction(kVFETrapFunctionName, T).getCallee(); +} + PreservedAnalyses GlobalDCEPass::run(Module &M, ModuleAnalysisManager &MAM) { bool Changed = false; @@ -350,6 +372,10 @@ // might call, if we have that information. AddVirtualFunctionDependencies(M); + auto *MDVal = mdconst::dyn_extract_or_null( + M.getModuleFlag("VFE Emit Trap Function")); + bool EmitTrapFunction = (MDVal && MDVal->getZExtValue() == 1); + // Loop over the module, adding globals which are obviously necessary. for (GlobalObject &GO : M.global_objects()) { Changed |= RemoveUnusedGlobalValue(GO); @@ -451,7 +477,12 @@ // virtual function pointers with null, allowing us to remove the // function itself. ++NumVFuncs; - F->replaceNonMetadataUsesWith(ConstantPointerNull::get(F->getType())); + if (EmitTrapFunction) { + auto FnType = cast(F->getType()->getElementType()); + F->replaceNonMetadataUsesWith(getOrCreateTrapFunction(M, FnType)); + } else { + F->replaceNonMetadataUsesWith(ConstantPointerNull::get(F->getType())); + } } EraseUnusedGlobalValue(F); } Index: llvm/test/Transforms/GlobalDCE/virtual-functions-trap.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/GlobalDCE/virtual-functions-trap.ll @@ -0,0 +1,66 @@ +; RUN: opt < %s -globaldce -S | FileCheck %s +; RUN: opt < %s -globaldce -globaldce -S | FileCheck %s +; RUN: opt < %s -globaldce -globaldce -globaldce -S | FileCheck %s + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +declare { i8*, i1 } @llvm.type.checked.load(i8*, i32, metadata) + +@vtableA = internal unnamed_addr constant { [2 x i32] } { [2 x i32] [ + i32 trunc (i64 sub (i64 ptrtoint (void ()* @vfunc1_live to i64), i64 ptrtoint ({ [2 x i32] }* @vtableA to i64)) to i32), + i32 trunc (i64 sub (i64 ptrtoint (void ()* @vfunc2_dead to i64), i64 ptrtoint ({ [2 x i32] }* @vtableA to i64)) to i32) +]}, align 8, !type !0, !type !1, !vcall_visibility !2 +!0 = !{i64 0, !"vfunc1.type"} +!1 = !{i64 4, !"vfunc2.type"} + +; CHECK: @vtableA = 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] }* @vtableA to i64)) to i32), +; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint (void ()* @___globaldce_vfe_trap to i64), i64 ptrtoint ({ [2 x i32] }* @vtableA to i64)) to i32) +; CHECK-SAME: ] }, align 8, !type !0, !type !1, !vcall_visibility + +@vtableB = internal unnamed_addr constant { [2 x i8*] } { [2 x i8*] [ + i8* bitcast (void ()* @vfunc3_live to i8*), + i8* bitcast (void ()* @vfunc4_dead to i8*) +]}, align 8, !type !3, !type !4, !vcall_visibility !2 +!3 = !{i64 0, !"vfunc3.type"} +!4 = !{i64 8, !"vfunc4.type"} + +; CHECK: @vtableB = internal unnamed_addr constant { [2 x i8*] } { [2 x i8*] [ +; CHECK-SAME: i8* bitcast (void ()* @vfunc3_live to i8*), +; CHECK-SAME: i8* bitcast (void ()* @___globaldce_vfe_trap to i8*) +; CHECK-SAME: ] }, align 8, !type !3, !type !4, !vcall_visibility + +define internal void @vfunc1_live() { + ret void +} + +define internal void @vfunc2_dead() { + ret void +} + +define internal void @vfunc3_live() { + ret void +} + +define internal void @vfunc4_dead() { + ret void +} + +define void @main() { + %1 = ptrtoint { [2 x i32] }* @vtableA to i64 ; to keep @vtableA alive + %2 = ptrtoint { [2 x i8*] }* @vtableB to i64 ; to keep @vtableB alive + %3 = tail call { i8*, i1 } @llvm.type.checked.load(i8* null, i32 0, metadata !"vfunc1.type") + %4 = tail call { i8*, i1 } @llvm.type.checked.load(i8* null, i32 0, metadata !"vfunc3.type") + ret void +} + +; CHECK: define internal void @___globaldce_vfe_trap() { +; CHECK: entry: +; CHECK: call void @llvm.trap() +; CHECK: unreachable +; CHECK: } + +!2 = !{i64 2} +!998 = !{i32 1, !"VFE Emit Trap Function", i32 1} +!999 = !{i32 1, !"Virtual Function Elim", i32 1} +!llvm.module.flags = !{!999, !998}