diff --git a/llvm/include/llvm/Transforms/IPO/WholeProgramDevirt.h b/llvm/include/llvm/Transforms/IPO/WholeProgramDevirt.h --- a/llvm/include/llvm/Transforms/IPO/WholeProgramDevirt.h +++ b/llvm/include/llvm/Transforms/IPO/WholeProgramDevirt.h @@ -118,14 +118,14 @@ // A virtual call target, i.e. an entry in a particular vtable. struct VirtualCallTarget { - VirtualCallTarget(Function *Fn, const TypeMemberInfo *TM); + VirtualCallTarget(GlobalValue *Fn, const TypeMemberInfo *TM); // For testing only. VirtualCallTarget(const TypeMemberInfo *TM, bool IsBigEndian) : Fn(nullptr), TM(TM), IsBigEndian(IsBigEndian), WasDevirt(false) {} - // The function stored in the vtable. - Function *Fn; + // The function (or an alias to a function) stored in the vtable. + GlobalValue *Fn; // A pointer to the type identifier member through which the pointer to Fn is // accessed. 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 @@ -313,9 +313,10 @@ } } -VirtualCallTarget::VirtualCallTarget(Function *Fn, const TypeMemberInfo *TM) +VirtualCallTarget::VirtualCallTarget(GlobalValue *Fn, const TypeMemberInfo *TM) : Fn(Fn), TM(TM), - IsBigEndian(Fn->getParent()->getDataLayout().isBigEndian()), WasDevirt(false) {} + IsBigEndian(Fn->getParent()->getDataLayout().isBigEndian()), + WasDevirt(false) {} namespace { @@ -1009,7 +1010,13 @@ if (!Ptr) return false; - auto Fn = dyn_cast(Ptr->stripPointerCasts()); + auto C = Ptr->stripPointerCasts(); + // Make sure this is a function or alias to a function. + auto Fn = dyn_cast(C); + auto A = dyn_cast(C); + if (!Fn && A) + Fn = dyn_cast(A->getAliasee()); + if (!Fn) return false; @@ -1026,7 +1033,11 @@ if (mustBeUnreachableFunction(Fn, ExportSummary)) continue; - TargetsForSlot.push_back({Fn, &TM}); + // Save the symbol used in the vtable to use as the devirtualization + // target. + auto GV = dyn_cast(C); + assert(GV); + TargetsForSlot.push_back({GV, &TM}); } // Give up if we couldn't find any targets. @@ -1207,7 +1218,7 @@ WholeProgramDevirtResolution *Res) { // See if the program contains a single implementation of this virtual // function. - Function *TheFn = TargetsForSlot[0].Fn; + auto *TheFn = TargetsForSlot[0].Fn; for (auto &&Target : TargetsForSlot) if (TheFn != Target.Fn) return false; @@ -1453,23 +1464,30 @@ // Evaluate each function and store the result in each target's RetVal // field. for (VirtualCallTarget &Target : TargetsForSlot) { - if (Target.Fn->arg_size() != Args.size() + 1) + // TODO: Skip for now if the vtable symbol was an alias to a function, + // need to evaluate whether it would be correct to analyze the aliasee + // function for this optimization. + auto Fn = dyn_cast(Target.Fn); + if (!Fn) + return false; + + if (Fn->arg_size() != Args.size() + 1) return false; Evaluator Eval(M.getDataLayout(), nullptr); SmallVector EvalArgs; EvalArgs.push_back( - Constant::getNullValue(Target.Fn->getFunctionType()->getParamType(0))); + Constant::getNullValue(Fn->getFunctionType()->getParamType(0))); for (unsigned I = 0; I != Args.size(); ++I) { - auto *ArgTy = dyn_cast( - Target.Fn->getFunctionType()->getParamType(I + 1)); + auto *ArgTy = + dyn_cast(Fn->getFunctionType()->getParamType(I + 1)); if (!ArgTy) return false; EvalArgs.push_back(ConstantInt::get(ArgTy, Args[I])); } Constant *RetVal; - if (!Eval.EvaluateFunction(Target.Fn, RetVal, EvalArgs) || + if (!Eval.EvaluateFunction(Fn, RetVal, EvalArgs) || !isa(RetVal)) return false; Target.RetVal = cast(RetVal)->getZExtValue(); @@ -1690,8 +1708,14 @@ bool DevirtModule::tryVirtualConstProp( MutableArrayRef TargetsForSlot, VTableSlotInfo &SlotInfo, WholeProgramDevirtResolution *Res, VTableSlot Slot) { + // TODO: Skip for now if the vtable symbol was an alias to a function, + // need to evaluate whether it would be correct to analyze the aliasee + // function for this optimization. + auto Fn = dyn_cast(TargetsForSlot[0].Fn); + if (!Fn) + return false; // This only works if the function returns an integer. - auto RetType = dyn_cast(TargetsForSlot[0].Fn->getReturnType()); + auto RetType = dyn_cast(Fn->getReturnType()); if (!RetType) return false; unsigned BitWidth = RetType->getBitWidth(); @@ -1709,11 +1733,18 @@ // inline all implementations of the virtual function into each call site, // rather than using function attributes to perform local optimization. for (VirtualCallTarget &Target : TargetsForSlot) { - if (Target.Fn->isDeclaration() || - !computeFunctionBodyMemoryAccess(*Target.Fn, AARGetter(*Target.Fn)) + // TODO: Skip for now if the vtable symbol was an alias to a function, + // need to evaluate whether it would be correct to analyze the aliasee + // function for this optimization. + auto Fn = dyn_cast(Target.Fn); + if (!Fn) + return false; + + if (Fn->isDeclaration() || + !computeFunctionBodyMemoryAccess(*Fn, AARGetter(*Fn)) .doesNotAccessMemory() || - Target.Fn->arg_empty() || !Target.Fn->arg_begin()->use_empty() || - Target.Fn->getReturnType() != RetType) + Fn->arg_empty() || !Fn->arg_begin()->use_empty() || + Fn->getReturnType() != RetType) return false; } @@ -2221,7 +2252,7 @@ // For each (type, offset) pair: bool DidVirtualConstProp = false; - std::map DevirtTargets; + std::map DevirtTargets; for (auto &S : CallSlots) { // Search each of the members of the type identifier for the virtual // function implementation at offset S.first.ByteOffset, and add to @@ -2276,7 +2307,14 @@ if (RemarksEnabled) { // Generate remarks for each devirtualized function. for (const auto &DT : DevirtTargets) { - Function *F = DT.second; + GlobalValue *GV = DT.second; + auto F = dyn_cast(GV); + if (!F) { + auto A = dyn_cast(GV); + assert(A && isa(A->getAliasee())); + F = dyn_cast(A->getAliasee()); + assert(F); + } using namespace ore; OREGetter(F).emit(OptimizationRemark(DEBUG_TYPE, "Devirtualized", F) diff --git a/llvm/test/ThinLTO/X86/devirt_function_alias2.ll b/llvm/test/ThinLTO/X86/devirt_function_alias2.ll --- a/llvm/test/ThinLTO/X86/devirt_function_alias2.ll +++ b/llvm/test/ThinLTO/X86/devirt_function_alias2.ll @@ -44,19 +44,17 @@ ; RUN: 2>&1 | FileCheck %s --check-prefix=REMARK ; RUN: llvm-dis %t4.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR1 -;; TODO: Enable the below lines once Regular LTO support for devirtualizing -;; aliases in the vtable is supported. ;; Test Regular LTO -; RUN opt -o %t5.o %s -; RUN llvm-lto2 run %t5.o -save-temps -pass-remarks=. \ -; RUN -whole-program-visibility \ -; RUN -o %t6 \ -; RUN -r=%t5.o,test,px \ -; RUN -r=%t5.o,_ZTV1D,px \ -; RUN -r=%t5.o,_ZN1D1mEi,px \ -; RUN -r=%t5.o,_ZN1D1mEiAlias,px \ -; RUN 2>&1 | FileCheck %s --check-prefix=REMARK -; RUN llvm-dis %t6.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR1 +; RUN: opt -o %t5.o %s +; RUN: llvm-lto2 run %t5.o -save-temps -pass-remarks=. \ +; RUN: -whole-program-visibility \ +; RUN: -o %t6 \ +; RUN: -r=%t5.o,test,px \ +; RUN: -r=%t5.o,_ZTV1D,px \ +; RUN: -r=%t5.o,_ZN1D1mEi,px \ +; RUN: -r=%t5.o,_ZN1D1mEiAlias,px \ +; RUN: 2>&1 | FileCheck %s --check-prefix=REMARK +; RUN: llvm-dis %t6.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR1 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-grtev4-linux-gnu"