diff --git a/llvm/lib/Analysis/TypeMetadataUtils.cpp b/llvm/lib/Analysis/TypeMetadataUtils.cpp --- a/llvm/lib/Analysis/TypeMetadataUtils.cpp +++ b/llvm/lib/Analysis/TypeMetadataUtils.cpp @@ -67,6 +67,10 @@ findLoadCallsAtConstantOffset(M, DevirtCalls, User, Offset + GEPOffset, CI, DT); } + } else if (auto *Call = dyn_cast(User)) { + if (Call->getIntrinsicID() == llvm::Intrinsic::load_relative) { + findCallsAtConstantOffset(DevirtCalls, nullptr, User, Offset, CI, DT); + } } } } diff --git a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp --- a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp +++ b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp @@ -1004,10 +1004,15 @@ return false; Constant *Ptr = getPointerAtOffset(TM.Bits->GV->getInitializer(), - TM.Offset + ByteOffset, M); + TM.Offset + ByteOffset, M, + TM.Bits->GV); if (!Ptr) return false; + if (auto *Equiv = dyn_cast(Ptr)) { + Ptr = Equiv->getGlobalValue(); + } + auto Fn = dyn_cast(Ptr->stripPointerCasts()); if (!Fn) return false; diff --git a/llvm/test/Transforms/WholeProgramDevirt/devirt-single-impl.ll b/llvm/test/Transforms/WholeProgramDevirt/devirt-single-impl.ll --- a/llvm/test/Transforms/WholeProgramDevirt/devirt-single-impl.ll +++ b/llvm/test/Transforms/WholeProgramDevirt/devirt-single-impl.ll @@ -4,31 +4,45 @@ ; RUN: opt -S -passes=wholeprogramdevirt -whole-program-visibility -pass-remarks=wholeprogramdevirt -stats %s 2>&1 | FileCheck %s target datalayout = "e-p:64:64" -target triple = "x86_64-unknown-linux-gnu" +target triple = "x86_64-unknown-fuchsia" ; CHECK: remark: devirt-single.cc:30:32: single-impl: devirtualized a call to vf ; CHECK: remark: devirt-single.cc:13:0: devirtualized vf ; CHECK-NOT: devirtualized -@vt1 = constant [1 x i8*] [i8* bitcast (void (i8*)* @vf to i8*)], !type !8 -@vt2 = constant [1 x i8*] [i8* bitcast (void (i8*)* @vf to i8*)], !type !8 +;@vt1 = constant [1 x i8*] [i8* bitcast (void (i8*)* @vf to i8*)], !type !8 define void @vf(i8* %this) #0 !dbg !7 { ret void } +declare i8* @llvm.load.relative.i32(i8*, i32) argmemonly nocallback nofree nosync nounwind readonly willreturn + +;%class.A = type { i32 (...)** } +@vt2 = private unnamed_addr constant [1 x i32] [ + i32 trunc (i64 sub (i64 ptrtoint (void (i8*)* dso_local_equivalent @vf to i64), i64 ptrtoint (i32* getelementptr inbounds ([1 x i32], [1 x i32]* @vt2, i32 0, i32 0) to i64)) to i32) +], align 4, !type !8 +;@vt2 = unnamed_addr alias { [3 x i32] }, { [3 x i32] }* @_ZTV1A.local +;@vt2 = constant [1 x i8*] [i8* bitcast (void (i8*)* @vf to i8*)], !type !8 + ; CHECK: define void @call define void @call(i8* %obj) #1 !dbg !5 { - %vtableptr = bitcast i8* %obj to [1 x i8*]** - %vtable = load [1 x i8*]*, [1 x i8*]** %vtableptr - %vtablei8 = bitcast [1 x i8*]* %vtable to i8* - %p = call i1 @llvm.type.test(i8* %vtablei8, metadata !"typeid") + %vtableptr = bitcast i8* %obj to i8** + %vtable = load i8*, i8** %vtableptr, align 8 + %p = call i1 @llvm.type.test(i8* %vtable, metadata !"typeid") call void @llvm.assume(i1 %p) - %fptrptr = getelementptr [1 x i8*], [1 x i8*]* %vtable, i32 0, i32 0 - %fptr = load i8*, i8** %fptrptr + + %fptr = call i8* @llvm.load.relative.i32(i8* %vtable, i32 0) %fptr_casted = bitcast i8* %fptr to void (i8*)* ; CHECK: call void @vf( - call void %fptr_casted(i8* %obj), !dbg !6 + call void %fptr_casted(i8* %obj) + + ;%fptrptr = bitcast i8* %vtable to i8** + ;%fptr = load i8*, i8** %fptrptr + ;%fptr_casted = bitcast i8* %fptr to void (i8*)* + ;;call void @vf( + ;call void %fptr_casted(i8* %obj), !dbg !6 + ret void }