Skip to content

Commit 6001a8b

Browse files
authored
[WholeProgramDevirt] Add check for AvailableExternal and give up icall.branch.funnel (#143468)
When a customer class inherits from a libc++ class, and is built with "-flto -fwhole-program-vtables -static-libstdc++ \ -Wl,-plugin-opt=-whole-program-visibility", the libc++ class's vtable is available_externally, meanwhile the customer class vtable is private. And both of them are !vcall_visibility == Linkage Unit. In this case, icall.branch.funnel might be generated. But the icall.branch.funnel would cause crash in LowerTypeTests because available_externally Global_Object's GlobalTypeMember would not be saved and finally leads to a NULL GlobalTypeMember which causes a crash. Even saving the available_externally GO's GlobalTypeMember so that it is not NULL to avoid the crash in LowerTypeTests, it still will crash in SelectionDAGBuilder or Verifier, because operands linkage type consistency check of icall.branch.funnel can not pass. So any one of available externally vtable would stop to generate icall.branch.funnel. This patch fixes FullLTO mode and split-LTO-unit ThinLTO mode.
1 parent dad6487 commit 6001a8b

File tree

2 files changed

+72
-0
lines changed

2 files changed

+72
-0
lines changed

llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1450,6 +1450,22 @@ void DevirtModule::tryICallBranchFunnel(
14501450
if (!HasNonDevirt)
14511451
return;
14521452

1453+
// If any GV is AvailableExternally, not to generate branch.funnel.
1454+
// NOTE: It is to avoid crash in LowerTypeTest.
1455+
// If the branch.funnel is generated, because GV.isDeclarationForLinker(),
1456+
// in LowerTypeTestsModule::lower(), its GlobalTypeMember would NOT
1457+
// be saved in GlobalTypeMembers[&GV]. Then crash happens in
1458+
// buildBitSetsFromDisjointSet due to GlobalTypeMembers[&GV] is NULL.
1459+
// Even doing experiment to save it in GlobalTypeMembers[&GV] and
1460+
// making GlobalTypeMembers[&GV] be not NULL, crash could avoid from
1461+
// buildBitSetsFromDisjointSet. But still report_fatal_error in Verifier
1462+
// or SelectionDAGBuilder later, because operands linkage type consistency
1463+
// check of icall.branch.funnel can not pass.
1464+
for (auto &T : TargetsForSlot) {
1465+
if (T.TM->Bits->GV->hasAvailableExternallyLinkage())
1466+
return;
1467+
}
1468+
14531469
FunctionType *FT =
14541470
FunctionType::get(Type::getVoidTy(M.getContext()), {Int8PtrTy}, true);
14551471
Function *JT;
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
; RUN: opt -S -passes=wholeprogramdevirt -whole-program-visibility %s | FileCheck %s
2+
3+
; This test is reduced from C++ code like this:
4+
; class A :public std::exception {
5+
; public:
6+
; A() {};
7+
; const char* what () const throw () {return "A";}
8+
; };
9+
; long test(std::exception *p) {
10+
; const char* ch = p->what();
11+
; ...;
12+
; }
13+
;
14+
; Build command is "clang++ -O2 -target x86_64-unknown-linux -flto=full \
15+
; -fwhole-program-vtables -static-libstdc++ -Wl,-plugin-opt=-whole-program-visibility"
16+
;
17+
; _ZTVSt9exception's visibility is 1 (Linkage Unit), and available_externally.
18+
; If any GV is available_externally, icall.branch.funnel should not be generated.
19+
20+
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"
21+
target triple = "x86_64-unknown-linux"
22+
23+
@_ZTVSt9exception = available_externally constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr null, ptr null, ptr null, ptr @_ZNKSt9exception4whatEv] }, !type !0, !type !1
24+
@_ZTV1A.0 = constant [5 x ptr] [ptr null, ptr null, ptr null, ptr null, ptr @_ZNK1A4whatEv], !type !3, !type !4, !type !5, !type !6
25+
26+
declare ptr @_ZNKSt9exception4whatEv()
27+
28+
define ptr @_Z4testPSt9exception() {
29+
%1 = load ptr, ptr null, align 8
30+
%2 = call i1 @llvm.type.test(ptr %1, metadata !"_ZTSSt9exception")
31+
tail call void @llvm.assume(i1 %2)
32+
%3 = getelementptr i8, ptr %1, i64 16
33+
%4 = load ptr, ptr %3, align 8
34+
%5 = tail call ptr %4(ptr null)
35+
ret ptr %5
36+
}
37+
38+
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
39+
declare void @llvm.assume(i1 noundef) #0
40+
41+
declare ptr @_ZNK1A4whatEv()
42+
43+
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
44+
declare i1 @llvm.type.test(ptr, metadata) #1
45+
46+
; CHECK-NOT: call void (...) @llvm.icall.branch.funnel
47+
48+
attributes #0 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }
49+
attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
50+
51+
!0 = !{i64 16, !"_ZTSSt9exception"}
52+
!1 = !{i64 32, !"_ZTSMSt9exceptionKDoFPKcvE.virtual"}
53+
!3 = !{i32 16, !"_ZTS1A"}
54+
!4 = !{i32 32, !"_ZTSM1AKDoFPKcvE.virtual"}
55+
!5 = !{i32 16, !"_ZTSSt9exception"}
56+
!6 = !{i32 32, !"_ZTSMSt9exceptionKDoFPKcvE.virtual"}

0 commit comments

Comments
 (0)