Index: llvm/lib/Transforms/IPO/GlobalDCE.cpp =================================================================== --- llvm/lib/Transforms/IPO/GlobalDCE.cpp +++ llvm/lib/Transforms/IPO/GlobalDCE.cpp @@ -161,14 +161,13 @@ /// Recursively iterate over the (sub-)constants in the vtable and look for /// vptrs, if their offset is within ranges in List, add them to VFuncs. static void FindVirtualFunctionsInVTable(Module &M, Constant *C, - GlobalObject::VCallVisibilityList List, + uint64_t RangeStart, uint64_t RangeEnd, SmallPtrSet *VFuncs, uint64_t BaseOffset = 0) { if (auto *GV = dyn_cast(C)) { if (auto *F = dyn_cast(GV)) - for (auto Entry : List) - if (Entry.RangeStart <= BaseOffset && BaseOffset < Entry.RangeEnd) - VFuncs->insert(F); + if (RangeStart <= BaseOffset && BaseOffset < RangeEnd) + VFuncs->insert(F); // Do not recurse outside of the current global. return; @@ -180,20 +179,22 @@ for (auto EI : llvm::enumerate(STy->elements())) { auto Offset = SL->getElementOffset(EI.index()); unsigned Op = SL->getElementContainingOffset(Offset); - FindVirtualFunctionsInVTable(M, cast(S->getOperand(Op)), List, - VFuncs, BaseOffset + Offset); + FindVirtualFunctionsInVTable(M, cast(S->getOperand(Op)), + RangeStart, RangeEnd, VFuncs, + BaseOffset + Offset); } } else if (auto *A = dyn_cast(C)) { ArrayType *ATy = A->getType(); auto EltSize = M.getDataLayout().getTypeAllocSize(ATy->getElementType()); for (unsigned i = 0, e = ATy->getNumElements(); i != e; ++i) { - FindVirtualFunctionsInVTable(M, cast(A->getOperand(i)), List, - VFuncs, BaseOffset + EltSize * i); + FindVirtualFunctionsInVTable(M, cast(A->getOperand(i)), + RangeStart, RangeEnd, VFuncs, + BaseOffset + EltSize * i); } } else { for (auto &Op : C->operands()) { - FindVirtualFunctionsInVTable(M, cast(Op), List, VFuncs, - BaseOffset); + FindVirtualFunctionsInVTable(M, cast(Op), RangeStart, RangeEnd, + VFuncs, BaseOffset); } } } @@ -233,28 +234,21 @@ // so VFE is safe. if (auto GO = dyn_cast(&GV)) { GlobalObject::VCallVisibilityList List = GO->getVCallVisibility(); - - // FIXME: For now, find the maximum visibility from all !vcall_visibility - // attachments, and conservatively treat all ranges with that visibility. - GlobalObject::VCallVisibility TypeVis = - GlobalObject::VCallVisibilityTranslationUnit; + SmallPtrSet VFuncs; for (auto Entry : List) { - TypeVis = std::min(TypeVis, Entry.Visibility); - } - if (List.empty()) - TypeVis = GlobalObject::VCallVisibilityPublic; - - if (TypeVis == GlobalObject::VCallVisibilityTranslationUnit || - (LTOPostLink && - TypeVis == GlobalObject::VCallVisibilityLinkageUnit)) { - LLVM_DEBUG(dbgs() << GV.getName() << " is safe for VFE\n"); - - // Find and record all the vfunctions that are within the offset ranges - // specified in the !vcall_visibility attributes. - SmallPtrSet VFuncs; - FindVirtualFunctionsInVTable(M, GV.getInitializer(), List, &VFuncs); - VFESafeVTablesAndFns[&GV] = VFuncs; + if (Entry.Visibility == GlobalObject::VCallVisibilityTranslationUnit || + (LTOPostLink && + Entry.Visibility == GlobalObject::VCallVisibilityLinkageUnit)) { + LLVM_DEBUG(dbgs() + << GV.getName() << " is safe for VFE, range " + << Entry.RangeStart << "," << Entry.RangeEnd << "\n"); + + // Find and record all the vfunctions that are within the offset range + FindVirtualFunctionsInVTable(M, GV.getInitializer(), Entry.RangeStart, + Entry.RangeEnd, &VFuncs); + } } + VFESafeVTablesAndFns[&GV] = VFuncs; } } } Index: llvm/test/Transforms/GlobalDCE/virtual-functions-ranges-post-lto.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/GlobalDCE/virtual-functions-ranges-post-lto.ll @@ -0,0 +1,48 @@ +; RUN: opt < %s -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) + +; A vtable that contains multiple ranges, range [0, 16) has LTO unit visibility, range [16, 32) has TU visibility +@vtable = internal unnamed_addr constant { [4 x i8*] } { [4 x i8*] [ + i8* bitcast (void ()* @vfunc1_live to i8*), + i8* bitcast (void ()* @vfunc2_dead to i8*), + i8* bitcast (void ()* @vfunc3_live to i8*), + i8* bitcast (void ()* @vfunc4_dead to i8*) +]}, align 8, + !type !{i64 0, !"vfunc1.type"}, !type !{i64 8, !"vfunc2.type"}, !type !{i64 16, !"vfunc3.type"}, !type !{i64 24, !"vfunc4.type"}, + !vcall_visibility !{i64 1, i64 0, i64 16}, !vcall_visibility !{i64 2, i64 16, i64 32} + +; This is a post-LTO version of the test, which means both the TU visibility range [16, 32) and the LTO unit visibility range [0, 16) are VFE safe + +; CHECK: @vtable = internal unnamed_addr constant { [4 x i8*] } { [4 x i8*] [ +; CHECK-SAME: i8* bitcast (void ()* @vfunc1_live to i8*), +; CHECK-SAME: i8* null, +; CHECK-SAME: i8* bitcast (void ()* @vfunc3_live to i8*), +; CHECK-SAME: i8* null +; CHECK-SAME: ] }, align 8 + +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 { [4 x i8*] }* @vtable to i64 ; to keep @vtable alive + %2 = tail call { i8*, i1 } @llvm.type.checked.load(i8* null, i32 0, metadata !"vfunc1.type") + %3 = tail call { i8*, i1 } @llvm.type.checked.load(i8* null, i32 0, metadata !"vfunc3.type") + ret void +} + +!998 = !{i32 1, !"LTOPostLink", i32 1} +!999 = !{i32 1, !"Virtual Function Elim", i32 1} +!llvm.module.flags = !{!998, !999} Index: llvm/test/Transforms/GlobalDCE/virtual-functions-ranges-pre-lto.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/GlobalDCE/virtual-functions-ranges-pre-lto.ll @@ -0,0 +1,47 @@ +; RUN: opt < %s -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) + +; A vtable that contains multiple ranges, range [0, 16) has LTO unit visibility, range [16, 32) has TU visibility +@vtable = internal unnamed_addr constant { [4 x i8*] } { [4 x i8*] [ + i8* bitcast (void ()* @vfunc1_live to i8*), + i8* bitcast (void ()* @vfunc2_dead to i8*), + i8* bitcast (void ()* @vfunc3_live to i8*), + i8* bitcast (void ()* @vfunc4_dead to i8*) +]}, align 8, + !type !{i64 0, !"vfunc1.type"}, !type !{i64 8, !"vfunc2.type"}, !type !{i64 16, !"vfunc3.type"}, !type !{i64 24, !"vfunc4.type"}, + !vcall_visibility !{i64 1, i64 0, i64 16}, !vcall_visibility !{i64 2, i64 16, i64 32} + +; This is a pre-LTO version of the test, which means only the TU visibility range [16, 32) is VFE safe + +; CHECK: @vtable = internal unnamed_addr constant { [4 x i8*] } { [4 x i8*] [ +; CHECK-SAME: i8* bitcast (void ()* @vfunc1_live to i8*), +; CHECK-SAME: i8* bitcast (void ()* @vfunc2_dead to i8*), +; CHECK-SAME: i8* bitcast (void ()* @vfunc3_live to i8*), +; CHECK-SAME: i8* null +; CHECK-SAME: ] }, align 8 + +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 { [4 x i8*] }* @vtable to i64 ; to keep @vtable alive + %2 = tail call { i8*, i1 } @llvm.type.checked.load(i8* null, i32 0, metadata !"vfunc1.type") + %3 = tail call { i8*, i1 } @llvm.type.checked.load(i8* null, i32 0, metadata !"vfunc3.type") + ret void +} + +!999 = !{i32 1, !"Virtual Function Elim", i32 1} +!llvm.module.flags = !{!999}