diff --git a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp index a7d9f3ba24b24..642c8e855fbab 100644 --- a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp +++ b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp @@ -1456,6 +1456,22 @@ void DevirtModule::tryICallBranchFunnel( if (!HasNonDevirt) return; + // If any GV is AvailableExternally, not to generate branch.funnel. + // NOTE: It is to avoid crash in LowerTypeTest. + // If the branch.funnel is generated, because GV.isDeclarationForLinker(), + // in LowerTypeTestsModule::lower(), its GlobalTypeMember would NOT + // be saved in GlobalTypeMembers[&GV]. Then crash happens in + // buildBitSetsFromDisjointSet due to GlobalTypeMembers[&GV] is NULL. + // Even doing experiment to save it in GlobalTypeMembers[&GV] and + // making GlobalTypeMembers[&GV] be not NULL, crash could avoid from + // buildBitSetsFromDisjointSet. But still report_fatal_error in Verifier + // or SelectionDAGBuilder later, because operands linkage type consistency + // check of icall.branch.funnel can not pass. + for (auto &T : TargetsForSlot) { + if (T.TM->Bits->GV->hasAvailableExternallyLinkage()) + return; + } + FunctionType *FT = FunctionType::get(Type::getVoidTy(M.getContext()), {Int8PtrTy}, true); Function *JT; diff --git a/llvm/test/Transforms/WholeProgramDevirt/availableexternal-check.ll b/llvm/test/Transforms/WholeProgramDevirt/availableexternal-check.ll new file mode 100644 index 0000000000000..41809b41d8c70 --- /dev/null +++ b/llvm/test/Transforms/WholeProgramDevirt/availableexternal-check.ll @@ -0,0 +1,56 @@ +; RUN: opt -S -passes=wholeprogramdevirt -whole-program-visibility %s | FileCheck %s + +; This test is reduced from C++ code like this: +; class A :public std::exception { +; public: +; A() {}; +; const char* what () const throw () {return "A";} +; }; +; long test(std::exception *p) { +; const char* ch = p->what(); +; ...; +; } +; +; Build command is "clang++ -O2 -target x86_64-unknown-linux -flto=full \ +; -fwhole-program-vtables -static-libstdc++ -Wl,-plugin-opt=-whole-program-visibility" +; +; _ZTVSt9exception's visibility is 1 (Linkage Unit), and available_externally. +; If any GV is available_externally, icall.branch.funnel should not be generated. + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux" + +@_ZTVSt9exception = available_externally constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr null, ptr null, ptr null, ptr @_ZNKSt9exception4whatEv] }, !type !0, !type !1 +@_ZTV1A.0 = constant [5 x ptr] [ptr null, ptr null, ptr null, ptr null, ptr @_ZNK1A4whatEv], !type !3, !type !4, !type !5, !type !6 + +declare ptr @_ZNKSt9exception4whatEv() + +define ptr @_Z4testPSt9exception() { + %1 = load ptr, ptr null, align 8 + %2 = call i1 @llvm.type.test(ptr %1, metadata !"_ZTSSt9exception") + tail call void @llvm.assume(i1 %2) + %3 = getelementptr i8, ptr %1, i64 16 + %4 = load ptr, ptr %3, align 8 + %5 = tail call ptr %4(ptr null) + ret ptr %5 +} + +; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) +declare void @llvm.assume(i1 noundef) #0 + +declare ptr @_ZNK1A4whatEv() + +; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) +declare i1 @llvm.type.test(ptr, metadata) #1 + +; CHECK-NOT: call void (...) @llvm.icall.branch.funnel + +attributes #0 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) } +attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } + +!0 = !{i64 16, !"_ZTSSt9exception"} +!1 = !{i64 32, !"_ZTSMSt9exceptionKDoFPKcvE.virtual"} +!3 = !{i32 16, !"_ZTS1A"} +!4 = !{i32 32, !"_ZTSM1AKDoFPKcvE.virtual"} +!5 = !{i32 16, !"_ZTSSt9exception"} +!6 = !{i32 32, !"_ZTSMSt9exceptionKDoFPKcvE.virtual"}