}
}
-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 {
if (!Ptr)
return false;
- auto Fn = dyn_cast<Function>(Ptr->stripPointerCasts());
+ auto C = Ptr->stripPointerCasts();
+ // Make sure this is a function or alias to a function.
+ auto Fn = dyn_cast<Function>(C);
+ auto A = dyn_cast<GlobalAlias>(C);
+ if (!Fn && A)
+ Fn = dyn_cast<Function>(A->getAliasee());
+
if (!Fn)
return false;
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<GlobalValue>(C);
+ assert(GV);
+ TargetsForSlot.push_back({GV, &TM});
}
// Give up if we couldn't find any targets.
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;
// 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<Function>(Target.Fn);
+ if (!Fn)
+ return false;
+
+ if (Fn->arg_size() != Args.size() + 1)
return false;
Evaluator Eval(M.getDataLayout(), nullptr);
SmallVector<Constant *, 2> 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<IntegerType>(
- Target.Fn->getFunctionType()->getParamType(I + 1));
+ auto *ArgTy =
+ dyn_cast<IntegerType>(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<ConstantInt>(RetVal))
return false;
Target.RetVal = cast<ConstantInt>(RetVal)->getZExtValue();
bool DevirtModule::tryVirtualConstProp(
MutableArrayRef<VirtualCallTarget> 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<Function>(TargetsForSlot[0].Fn);
+ if (!Fn)
+ return false;
// This only works if the function returns an integer.
- auto RetType = dyn_cast<IntegerType>(TargetsForSlot[0].Fn->getReturnType());
+ auto RetType = dyn_cast<IntegerType>(Fn->getReturnType());
if (!RetType)
return false;
unsigned BitWidth = RetType->getBitWidth();
// 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<Function>(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;
}
// For each (type, offset) pair:
bool DidVirtualConstProp = false;
- std::map<std::string, Function*> DevirtTargets;
+ std::map<std::string, GlobalValue *> 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
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<Function>(GV);
+ if (!F) {
+ auto A = dyn_cast<GlobalAlias>(GV);
+ assert(A && isa<Function>(A->getAliasee()));
+ F = dyn_cast<Function>(A->getAliasee());
+ assert(F);
+ }
using namespace ore;
OREGetter(F).emit(OptimizationRemark(DEBUG_TYPE, "Devirtualized", F)
; 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"