Skip to content

Commit c1b3e88

Browse files
committed
[LTO/WPD] Allow devirtualization to function alias in vtable
Follow on to D144209 to support single implementation devirtualization for Regular LTO when the vtable holds a function alias. For now I have prevented other optimizations performed in regular LTO that need to analyze the contents of the function target when the vtable holds an alias, as I'm not sure they are always correct to perform in that case. Differential Revision: https://reviews.llvm.org/D144270
1 parent 7b7db78 commit c1b3e88

File tree

3 files changed

+68
-32
lines changed

3 files changed

+68
-32
lines changed

llvm/include/llvm/Transforms/IPO/WholeProgramDevirt.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,14 @@ struct TypeMemberInfo {
118118

119119
// A virtual call target, i.e. an entry in a particular vtable.
120120
struct VirtualCallTarget {
121-
VirtualCallTarget(Function *Fn, const TypeMemberInfo *TM);
121+
VirtualCallTarget(GlobalValue *Fn, const TypeMemberInfo *TM);
122122

123123
// For testing only.
124124
VirtualCallTarget(const TypeMemberInfo *TM, bool IsBigEndian)
125125
: Fn(nullptr), TM(TM), IsBigEndian(IsBigEndian), WasDevirt(false) {}
126126

127-
// The function stored in the vtable.
128-
Function *Fn;
127+
// The function (or an alias to a function) stored in the vtable.
128+
GlobalValue *Fn;
129129

130130
// A pointer to the type identifier member through which the pointer to Fn is
131131
// accessed.

llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -313,9 +313,10 @@ void wholeprogramdevirt::setAfterReturnValues(
313313
}
314314
}
315315

316-
VirtualCallTarget::VirtualCallTarget(Function *Fn, const TypeMemberInfo *TM)
316+
VirtualCallTarget::VirtualCallTarget(GlobalValue *Fn, const TypeMemberInfo *TM)
317317
: Fn(Fn), TM(TM),
318-
IsBigEndian(Fn->getParent()->getDataLayout().isBigEndian()), WasDevirt(false) {}
318+
IsBigEndian(Fn->getParent()->getDataLayout().isBigEndian()),
319+
WasDevirt(false) {}
319320

320321
namespace {
321322

@@ -1009,7 +1010,13 @@ bool DevirtModule::tryFindVirtualCallTargets(
10091010
if (!Ptr)
10101011
return false;
10111012

1012-
auto Fn = dyn_cast<Function>(Ptr->stripPointerCasts());
1013+
auto C = Ptr->stripPointerCasts();
1014+
// Make sure this is a function or alias to a function.
1015+
auto Fn = dyn_cast<Function>(C);
1016+
auto A = dyn_cast<GlobalAlias>(C);
1017+
if (!Fn && A)
1018+
Fn = dyn_cast<Function>(A->getAliasee());
1019+
10131020
if (!Fn)
10141021
return false;
10151022

@@ -1026,7 +1033,11 @@ bool DevirtModule::tryFindVirtualCallTargets(
10261033
if (mustBeUnreachableFunction(Fn, ExportSummary))
10271034
continue;
10281035

1029-
TargetsForSlot.push_back({Fn, &TM});
1036+
// Save the symbol used in the vtable to use as the devirtualization
1037+
// target.
1038+
auto GV = dyn_cast<GlobalValue>(C);
1039+
assert(GV);
1040+
TargetsForSlot.push_back({GV, &TM});
10301041
}
10311042

10321043
// Give up if we couldn't find any targets.
@@ -1207,7 +1218,7 @@ bool DevirtModule::trySingleImplDevirt(
12071218
WholeProgramDevirtResolution *Res) {
12081219
// See if the program contains a single implementation of this virtual
12091220
// function.
1210-
Function *TheFn = TargetsForSlot[0].Fn;
1221+
auto *TheFn = TargetsForSlot[0].Fn;
12111222
for (auto &&Target : TargetsForSlot)
12121223
if (TheFn != Target.Fn)
12131224
return false;
@@ -1453,23 +1464,30 @@ bool DevirtModule::tryEvaluateFunctionsWithArgs(
14531464
// Evaluate each function and store the result in each target's RetVal
14541465
// field.
14551466
for (VirtualCallTarget &Target : TargetsForSlot) {
1456-
if (Target.Fn->arg_size() != Args.size() + 1)
1467+
// TODO: Skip for now if the vtable symbol was an alias to a function,
1468+
// need to evaluate whether it would be correct to analyze the aliasee
1469+
// function for this optimization.
1470+
auto Fn = dyn_cast<Function>(Target.Fn);
1471+
if (!Fn)
1472+
return false;
1473+
1474+
if (Fn->arg_size() != Args.size() + 1)
14571475
return false;
14581476

14591477
Evaluator Eval(M.getDataLayout(), nullptr);
14601478
SmallVector<Constant *, 2> EvalArgs;
14611479
EvalArgs.push_back(
1462-
Constant::getNullValue(Target.Fn->getFunctionType()->getParamType(0)));
1480+
Constant::getNullValue(Fn->getFunctionType()->getParamType(0)));
14631481
for (unsigned I = 0; I != Args.size(); ++I) {
1464-
auto *ArgTy = dyn_cast<IntegerType>(
1465-
Target.Fn->getFunctionType()->getParamType(I + 1));
1482+
auto *ArgTy =
1483+
dyn_cast<IntegerType>(Fn->getFunctionType()->getParamType(I + 1));
14661484
if (!ArgTy)
14671485
return false;
14681486
EvalArgs.push_back(ConstantInt::get(ArgTy, Args[I]));
14691487
}
14701488

14711489
Constant *RetVal;
1472-
if (!Eval.EvaluateFunction(Target.Fn, RetVal, EvalArgs) ||
1490+
if (!Eval.EvaluateFunction(Fn, RetVal, EvalArgs) ||
14731491
!isa<ConstantInt>(RetVal))
14741492
return false;
14751493
Target.RetVal = cast<ConstantInt>(RetVal)->getZExtValue();
@@ -1690,8 +1708,14 @@ void DevirtModule::applyVirtualConstProp(CallSiteInfo &CSInfo, StringRef FnName,
16901708
bool DevirtModule::tryVirtualConstProp(
16911709
MutableArrayRef<VirtualCallTarget> TargetsForSlot, VTableSlotInfo &SlotInfo,
16921710
WholeProgramDevirtResolution *Res, VTableSlot Slot) {
1711+
// TODO: Skip for now if the vtable symbol was an alias to a function,
1712+
// need to evaluate whether it would be correct to analyze the aliasee
1713+
// function for this optimization.
1714+
auto Fn = dyn_cast<Function>(TargetsForSlot[0].Fn);
1715+
if (!Fn)
1716+
return false;
16931717
// This only works if the function returns an integer.
1694-
auto RetType = dyn_cast<IntegerType>(TargetsForSlot[0].Fn->getReturnType());
1718+
auto RetType = dyn_cast<IntegerType>(Fn->getReturnType());
16951719
if (!RetType)
16961720
return false;
16971721
unsigned BitWidth = RetType->getBitWidth();
@@ -1709,11 +1733,18 @@ bool DevirtModule::tryVirtualConstProp(
17091733
// inline all implementations of the virtual function into each call site,
17101734
// rather than using function attributes to perform local optimization.
17111735
for (VirtualCallTarget &Target : TargetsForSlot) {
1712-
if (Target.Fn->isDeclaration() ||
1713-
!computeFunctionBodyMemoryAccess(*Target.Fn, AARGetter(*Target.Fn))
1736+
// TODO: Skip for now if the vtable symbol was an alias to a function,
1737+
// need to evaluate whether it would be correct to analyze the aliasee
1738+
// function for this optimization.
1739+
auto Fn = dyn_cast<Function>(Target.Fn);
1740+
if (!Fn)
1741+
return false;
1742+
1743+
if (Fn->isDeclaration() ||
1744+
!computeFunctionBodyMemoryAccess(*Fn, AARGetter(*Fn))
17141745
.doesNotAccessMemory() ||
1715-
Target.Fn->arg_empty() || !Target.Fn->arg_begin()->use_empty() ||
1716-
Target.Fn->getReturnType() != RetType)
1746+
Fn->arg_empty() || !Fn->arg_begin()->use_empty() ||
1747+
Fn->getReturnType() != RetType)
17171748
return false;
17181749
}
17191750

@@ -2221,7 +2252,7 @@ bool DevirtModule::run() {
22212252

22222253
// For each (type, offset) pair:
22232254
bool DidVirtualConstProp = false;
2224-
std::map<std::string, Function*> DevirtTargets;
2255+
std::map<std::string, GlobalValue *> DevirtTargets;
22252256
for (auto &S : CallSlots) {
22262257
// Search each of the members of the type identifier for the virtual
22272258
// function implementation at offset S.first.ByteOffset, and add to
@@ -2276,7 +2307,14 @@ bool DevirtModule::run() {
22762307
if (RemarksEnabled) {
22772308
// Generate remarks for each devirtualized function.
22782309
for (const auto &DT : DevirtTargets) {
2279-
Function *F = DT.second;
2310+
GlobalValue *GV = DT.second;
2311+
auto F = dyn_cast<Function>(GV);
2312+
if (!F) {
2313+
auto A = dyn_cast<GlobalAlias>(GV);
2314+
assert(A && isa<Function>(A->getAliasee()));
2315+
F = dyn_cast<Function>(A->getAliasee());
2316+
assert(F);
2317+
}
22802318

22812319
using namespace ore;
22822320
OREGetter(F).emit(OptimizationRemark(DEBUG_TYPE, "Devirtualized", F)

llvm/test/ThinLTO/X86/devirt_function_alias2.ll

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,17 @@
4444
; RUN: 2>&1 | FileCheck %s --check-prefix=REMARK
4545
; RUN: llvm-dis %t4.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR1
4646

47-
;; TODO: Enable the below lines once Regular LTO support for devirtualizing
48-
;; aliases in the vtable is supported.
4947
;; Test Regular LTO
50-
; RUN opt -o %t5.o %s
51-
; RUN llvm-lto2 run %t5.o -save-temps -pass-remarks=. \
52-
; RUN -whole-program-visibility \
53-
; RUN -o %t6 \
54-
; RUN -r=%t5.o,test,px \
55-
; RUN -r=%t5.o,_ZTV1D,px \
56-
; RUN -r=%t5.o,_ZN1D1mEi,px \
57-
; RUN -r=%t5.o,_ZN1D1mEiAlias,px \
58-
; RUN 2>&1 | FileCheck %s --check-prefix=REMARK
59-
; RUN llvm-dis %t6.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR1
48+
; RUN: opt -o %t5.o %s
49+
; RUN: llvm-lto2 run %t5.o -save-temps -pass-remarks=. \
50+
; RUN: -whole-program-visibility \
51+
; RUN: -o %t6 \
52+
; RUN: -r=%t5.o,test,px \
53+
; RUN: -r=%t5.o,_ZTV1D,px \
54+
; RUN: -r=%t5.o,_ZN1D1mEi,px \
55+
; RUN: -r=%t5.o,_ZN1D1mEiAlias,px \
56+
; RUN: 2>&1 | FileCheck %s --check-prefix=REMARK
57+
; RUN: llvm-dis %t6.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR1
6058

6159
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
6260
target triple = "x86_64-grtev4-linux-gnu"

0 commit comments

Comments
 (0)