10b57cec5SDimitry Andric //===- WholeProgramDevirt.cpp - Whole program virtual call optimization ---===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This pass implements whole program optimization of virtual calls in cases
100b57cec5SDimitry Andric // where we know (via !type metadata) that the list of callees is fixed. This
110b57cec5SDimitry Andric // includes the following:
120b57cec5SDimitry Andric // - Single implementation devirtualization: if a virtual call has a single
130b57cec5SDimitry Andric // possible callee, replace all calls with a direct call to that callee.
140b57cec5SDimitry Andric // - Virtual constant propagation: if the virtual function's return type is an
150b57cec5SDimitry Andric // integer <=64 bits and all possible callees are readnone, for each class and
160b57cec5SDimitry Andric // each list of constant arguments: evaluate the function, store the return
170b57cec5SDimitry Andric // value alongside the virtual table, and rewrite each virtual call as a load
180b57cec5SDimitry Andric // from the virtual table.
190b57cec5SDimitry Andric // - Uniform return value optimization: if the conditions for virtual constant
200b57cec5SDimitry Andric // propagation hold and each function returns the same constant value, replace
210b57cec5SDimitry Andric // each virtual call with that constant.
220b57cec5SDimitry Andric // - Unique return value optimization for i1 return values: if the conditions
230b57cec5SDimitry Andric // for virtual constant propagation hold and a single vtable's function
240b57cec5SDimitry Andric // returns 0, or a single vtable's function returns 1, replace each virtual
250b57cec5SDimitry Andric // call with a comparison of the vptr against that vtable's address.
260b57cec5SDimitry Andric //
278bcb0991SDimitry Andric // This pass is intended to be used during the regular and thin LTO pipelines:
288bcb0991SDimitry Andric //
290b57cec5SDimitry Andric // During regular LTO, the pass determines the best optimization for each
300b57cec5SDimitry Andric // virtual call and applies the resolutions directly to virtual calls that are
310b57cec5SDimitry Andric // eligible for virtual call optimization (i.e. calls that use either of the
328bcb0991SDimitry Andric // llvm.assume(llvm.type.test) or llvm.type.checked.load intrinsics).
338bcb0991SDimitry Andric //
348bcb0991SDimitry Andric // During hybrid Regular/ThinLTO, the pass operates in two phases:
350b57cec5SDimitry Andric // - Export phase: this is run during the thin link over a single merged module
360b57cec5SDimitry Andric // that contains all vtables with !type metadata that participate in the link.
370b57cec5SDimitry Andric // The pass computes a resolution for each virtual call and stores it in the
380b57cec5SDimitry Andric // type identifier summary.
390b57cec5SDimitry Andric // - Import phase: this is run during the thin backends over the individual
400b57cec5SDimitry Andric // modules. The pass applies the resolutions previously computed during the
410b57cec5SDimitry Andric // import phase to each eligible virtual call.
420b57cec5SDimitry Andric //
438bcb0991SDimitry Andric // During ThinLTO, the pass operates in two phases:
448bcb0991SDimitry Andric // - Export phase: this is run during the thin link over the index which
458bcb0991SDimitry Andric // contains a summary of all vtables with !type metadata that participate in
468bcb0991SDimitry Andric // the link. It computes a resolution for each virtual call and stores it in
478bcb0991SDimitry Andric // the type identifier summary. Only single implementation devirtualization
488bcb0991SDimitry Andric // is supported.
498bcb0991SDimitry Andric // - Import phase: (same as with hybrid case above).
508bcb0991SDimitry Andric //
510b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
520b57cec5SDimitry Andric
530b57cec5SDimitry Andric #include "llvm/Transforms/IPO/WholeProgramDevirt.h"
540b57cec5SDimitry Andric #include "llvm/ADT/ArrayRef.h"
550b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h"
560b57cec5SDimitry Andric #include "llvm/ADT/DenseMapInfo.h"
570b57cec5SDimitry Andric #include "llvm/ADT/DenseSet.h"
580b57cec5SDimitry Andric #include "llvm/ADT/MapVector.h"
590b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h"
6081ad6265SDimitry Andric #include "llvm/ADT/Statistic.h"
61e8d8bef9SDimitry Andric #include "llvm/Analysis/AssumptionCache.h"
620b57cec5SDimitry Andric #include "llvm/Analysis/BasicAliasAnalysis.h"
630b57cec5SDimitry Andric #include "llvm/Analysis/OptimizationRemarkEmitter.h"
640b57cec5SDimitry Andric #include "llvm/Analysis/TypeMetadataUtils.h"
655ffd83dbSDimitry Andric #include "llvm/Bitcode/BitcodeReader.h"
665ffd83dbSDimitry Andric #include "llvm/Bitcode/BitcodeWriter.h"
670b57cec5SDimitry Andric #include "llvm/IR/Constants.h"
680b57cec5SDimitry Andric #include "llvm/IR/DataLayout.h"
690b57cec5SDimitry Andric #include "llvm/IR/DebugLoc.h"
700b57cec5SDimitry Andric #include "llvm/IR/DerivedTypes.h"
710b57cec5SDimitry Andric #include "llvm/IR/Dominators.h"
720b57cec5SDimitry Andric #include "llvm/IR/Function.h"
730b57cec5SDimitry Andric #include "llvm/IR/GlobalAlias.h"
740b57cec5SDimitry Andric #include "llvm/IR/GlobalVariable.h"
750b57cec5SDimitry Andric #include "llvm/IR/IRBuilder.h"
760b57cec5SDimitry Andric #include "llvm/IR/InstrTypes.h"
770b57cec5SDimitry Andric #include "llvm/IR/Instruction.h"
780b57cec5SDimitry Andric #include "llvm/IR/Instructions.h"
790b57cec5SDimitry Andric #include "llvm/IR/Intrinsics.h"
800b57cec5SDimitry Andric #include "llvm/IR/LLVMContext.h"
8181ad6265SDimitry Andric #include "llvm/IR/MDBuilder.h"
820b57cec5SDimitry Andric #include "llvm/IR/Metadata.h"
830b57cec5SDimitry Andric #include "llvm/IR/Module.h"
840b57cec5SDimitry Andric #include "llvm/IR/ModuleSummaryIndexYAML.h"
850b57cec5SDimitry Andric #include "llvm/Support/Casting.h"
86480093f4SDimitry Andric #include "llvm/Support/CommandLine.h"
875ffd83dbSDimitry Andric #include "llvm/Support/Errc.h"
880b57cec5SDimitry Andric #include "llvm/Support/Error.h"
890b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h"
905ffd83dbSDimitry Andric #include "llvm/Support/GlobPattern.h"
910b57cec5SDimitry Andric #include "llvm/Support/MathExtras.h"
9206c3fb27SDimitry Andric #include "llvm/TargetParser/Triple.h"
930b57cec5SDimitry Andric #include "llvm/Transforms/IPO.h"
940b57cec5SDimitry Andric #include "llvm/Transforms/IPO/FunctionAttrs.h"
95fe6060f1SDimitry Andric #include "llvm/Transforms/Utils/BasicBlockUtils.h"
9681ad6265SDimitry Andric #include "llvm/Transforms/Utils/CallPromotionUtils.h"
970b57cec5SDimitry Andric #include "llvm/Transforms/Utils/Evaluator.h"
980b57cec5SDimitry Andric #include <algorithm>
990b57cec5SDimitry Andric #include <cstddef>
1000b57cec5SDimitry Andric #include <map>
1010b57cec5SDimitry Andric #include <set>
1020b57cec5SDimitry Andric #include <string>
1030b57cec5SDimitry Andric
1040b57cec5SDimitry Andric using namespace llvm;
1050b57cec5SDimitry Andric using namespace wholeprogramdevirt;
1060b57cec5SDimitry Andric
1070b57cec5SDimitry Andric #define DEBUG_TYPE "wholeprogramdevirt"
1080b57cec5SDimitry Andric
10981ad6265SDimitry Andric STATISTIC(NumDevirtTargets, "Number of whole program devirtualization targets");
11081ad6265SDimitry Andric STATISTIC(NumSingleImpl, "Number of single implementation devirtualizations");
11181ad6265SDimitry Andric STATISTIC(NumBranchFunnel, "Number of branch funnels");
11281ad6265SDimitry Andric STATISTIC(NumUniformRetVal, "Number of uniform return value optimizations");
11381ad6265SDimitry Andric STATISTIC(NumUniqueRetVal, "Number of unique return value optimizations");
11481ad6265SDimitry Andric STATISTIC(NumVirtConstProp1Bit,
11581ad6265SDimitry Andric "Number of 1 bit virtual constant propagations");
11681ad6265SDimitry Andric STATISTIC(NumVirtConstProp, "Number of virtual constant propagations");
11781ad6265SDimitry Andric
1180b57cec5SDimitry Andric static cl::opt<PassSummaryAction> ClSummaryAction(
1190b57cec5SDimitry Andric "wholeprogramdevirt-summary-action",
1200b57cec5SDimitry Andric cl::desc("What to do with the summary when running this pass"),
1210b57cec5SDimitry Andric cl::values(clEnumValN(PassSummaryAction::None, "none", "Do nothing"),
1220b57cec5SDimitry Andric clEnumValN(PassSummaryAction::Import, "import",
1230b57cec5SDimitry Andric "Import typeid resolutions from summary and globals"),
1240b57cec5SDimitry Andric clEnumValN(PassSummaryAction::Export, "export",
1250b57cec5SDimitry Andric "Export typeid resolutions to summary and globals")),
1260b57cec5SDimitry Andric cl::Hidden);
1270b57cec5SDimitry Andric
1280b57cec5SDimitry Andric static cl::opt<std::string> ClReadSummary(
1290b57cec5SDimitry Andric "wholeprogramdevirt-read-summary",
1305ffd83dbSDimitry Andric cl::desc(
1315ffd83dbSDimitry Andric "Read summary from given bitcode or YAML file before running pass"),
1320b57cec5SDimitry Andric cl::Hidden);
1330b57cec5SDimitry Andric
1340b57cec5SDimitry Andric static cl::opt<std::string> ClWriteSummary(
1350b57cec5SDimitry Andric "wholeprogramdevirt-write-summary",
1365ffd83dbSDimitry Andric cl::desc("Write summary to given bitcode or YAML file after running pass. "
1375ffd83dbSDimitry Andric "Output file format is deduced from extension: *.bc means writing "
1385ffd83dbSDimitry Andric "bitcode, otherwise YAML"),
1390b57cec5SDimitry Andric cl::Hidden);
1400b57cec5SDimitry Andric
1410b57cec5SDimitry Andric static cl::opt<unsigned>
1420b57cec5SDimitry Andric ClThreshold("wholeprogramdevirt-branch-funnel-threshold", cl::Hidden,
14381ad6265SDimitry Andric cl::init(10),
1440b57cec5SDimitry Andric cl::desc("Maximum number of call targets per "
1450b57cec5SDimitry Andric "call site to enable branch funnels"));
1460b57cec5SDimitry Andric
1478bcb0991SDimitry Andric static cl::opt<bool>
1488bcb0991SDimitry Andric PrintSummaryDevirt("wholeprogramdevirt-print-index-based", cl::Hidden,
1498bcb0991SDimitry Andric cl::desc("Print index-based devirtualization messages"));
1508bcb0991SDimitry Andric
1515ffd83dbSDimitry Andric /// Provide a way to force enable whole program visibility in tests.
1525ffd83dbSDimitry Andric /// This is needed to support legacy tests that don't contain
1535ffd83dbSDimitry Andric /// !vcall_visibility metadata (the mere presense of type tests
1545ffd83dbSDimitry Andric /// previously implied hidden visibility).
155fe6060f1SDimitry Andric static cl::opt<bool>
15681ad6265SDimitry Andric WholeProgramVisibility("whole-program-visibility", cl::Hidden,
1575ffd83dbSDimitry Andric cl::desc("Enable whole program visibility"));
1585ffd83dbSDimitry Andric
1595ffd83dbSDimitry Andric /// Provide a way to force disable whole program for debugging or workarounds,
1605ffd83dbSDimitry Andric /// when enabled via the linker.
161fe6060f1SDimitry Andric static cl::opt<bool> DisableWholeProgramVisibility(
16281ad6265SDimitry Andric "disable-whole-program-visibility", cl::Hidden,
1635ffd83dbSDimitry Andric cl::desc("Disable whole program visibility (overrides enabling options)"));
1645ffd83dbSDimitry Andric
1655ffd83dbSDimitry Andric /// Provide way to prevent certain function from being devirtualized
166fe6060f1SDimitry Andric static cl::list<std::string>
1675ffd83dbSDimitry Andric SkipFunctionNames("wholeprogramdevirt-skip",
1685ffd83dbSDimitry Andric cl::desc("Prevent function(s) from being devirtualized"),
16981ad6265SDimitry Andric cl::Hidden, cl::CommaSeparated);
1705ffd83dbSDimitry Andric
17181ad6265SDimitry Andric /// Mechanism to add runtime checking of devirtualization decisions, optionally
17281ad6265SDimitry Andric /// trapping or falling back to indirect call on any that are not correct.
17381ad6265SDimitry Andric /// Trapping mode is useful for debugging undefined behavior leading to failures
17481ad6265SDimitry Andric /// with WPD. Fallback mode is useful for ensuring safety when whole program
17581ad6265SDimitry Andric /// visibility may be compromised.
17681ad6265SDimitry Andric enum WPDCheckMode { None, Trap, Fallback };
17781ad6265SDimitry Andric static cl::opt<WPDCheckMode> DevirtCheckMode(
17881ad6265SDimitry Andric "wholeprogramdevirt-check", cl::Hidden,
17981ad6265SDimitry Andric cl::desc("Type of checking for incorrect devirtualizations"),
18081ad6265SDimitry Andric cl::values(clEnumValN(WPDCheckMode::None, "none", "No checking"),
18181ad6265SDimitry Andric clEnumValN(WPDCheckMode::Trap, "trap", "Trap when incorrect"),
18281ad6265SDimitry Andric clEnumValN(WPDCheckMode::Fallback, "fallback",
18381ad6265SDimitry Andric "Fallback to indirect when incorrect")));
184fe6060f1SDimitry Andric
1855ffd83dbSDimitry Andric namespace {
1865ffd83dbSDimitry Andric struct PatternList {
1875ffd83dbSDimitry Andric std::vector<GlobPattern> Patterns;
init__anone706c94a0111::PatternList1885ffd83dbSDimitry Andric template <class T> void init(const T &StringList) {
1895ffd83dbSDimitry Andric for (const auto &S : StringList)
1905ffd83dbSDimitry Andric if (Expected<GlobPattern> Pat = GlobPattern::create(S))
1915ffd83dbSDimitry Andric Patterns.push_back(std::move(*Pat));
1925ffd83dbSDimitry Andric }
match__anone706c94a0111::PatternList1935ffd83dbSDimitry Andric bool match(StringRef S) {
1945ffd83dbSDimitry Andric for (const GlobPattern &P : Patterns)
1955ffd83dbSDimitry Andric if (P.match(S))
1965ffd83dbSDimitry Andric return true;
1975ffd83dbSDimitry Andric return false;
1985ffd83dbSDimitry Andric }
1995ffd83dbSDimitry Andric };
2005ffd83dbSDimitry Andric } // namespace
2015ffd83dbSDimitry Andric
2020b57cec5SDimitry Andric // Find the minimum offset that we may store a value of size Size bits at. If
2030b57cec5SDimitry Andric // IsAfter is set, look for an offset before the object, otherwise look for an
2040b57cec5SDimitry Andric // offset after the object.
2050b57cec5SDimitry Andric uint64_t
findLowestOffset(ArrayRef<VirtualCallTarget> Targets,bool IsAfter,uint64_t Size)2060b57cec5SDimitry Andric wholeprogramdevirt::findLowestOffset(ArrayRef<VirtualCallTarget> Targets,
2070b57cec5SDimitry Andric bool IsAfter, uint64_t Size) {
2080b57cec5SDimitry Andric // Find a minimum offset taking into account only vtable sizes.
2090b57cec5SDimitry Andric uint64_t MinByte = 0;
2100b57cec5SDimitry Andric for (const VirtualCallTarget &Target : Targets) {
2110b57cec5SDimitry Andric if (IsAfter)
2120b57cec5SDimitry Andric MinByte = std::max(MinByte, Target.minAfterBytes());
2130b57cec5SDimitry Andric else
2140b57cec5SDimitry Andric MinByte = std::max(MinByte, Target.minBeforeBytes());
2150b57cec5SDimitry Andric }
2160b57cec5SDimitry Andric
2170b57cec5SDimitry Andric // Build a vector of arrays of bytes covering, for each target, a slice of the
2180b57cec5SDimitry Andric // used region (see AccumBitVector::BytesUsed in
2190b57cec5SDimitry Andric // llvm/Transforms/IPO/WholeProgramDevirt.h) starting at MinByte. Effectively,
2200b57cec5SDimitry Andric // this aligns the used regions to start at MinByte.
2210b57cec5SDimitry Andric //
2220b57cec5SDimitry Andric // In this example, A, B and C are vtables, # is a byte already allocated for
2230b57cec5SDimitry Andric // a virtual function pointer, AAAA... (etc.) are the used regions for the
2240b57cec5SDimitry Andric // vtables and Offset(X) is the value computed for the Offset variable below
2250b57cec5SDimitry Andric // for X.
2260b57cec5SDimitry Andric //
2270b57cec5SDimitry Andric // Offset(A)
2280b57cec5SDimitry Andric // | |
2290b57cec5SDimitry Andric // |MinByte
2300b57cec5SDimitry Andric // A: ################AAAAAAAA|AAAAAAAA
2310b57cec5SDimitry Andric // B: ########BBBBBBBBBBBBBBBB|BBBB
2320b57cec5SDimitry Andric // C: ########################|CCCCCCCCCCCCCCCC
2330b57cec5SDimitry Andric // | Offset(B) |
2340b57cec5SDimitry Andric //
2350b57cec5SDimitry Andric // This code produces the slices of A, B and C that appear after the divider
2360b57cec5SDimitry Andric // at MinByte.
2370b57cec5SDimitry Andric std::vector<ArrayRef<uint8_t>> Used;
2380b57cec5SDimitry Andric for (const VirtualCallTarget &Target : Targets) {
2390b57cec5SDimitry Andric ArrayRef<uint8_t> VTUsed = IsAfter ? Target.TM->Bits->After.BytesUsed
2400b57cec5SDimitry Andric : Target.TM->Bits->Before.BytesUsed;
2410b57cec5SDimitry Andric uint64_t Offset = IsAfter ? MinByte - Target.minAfterBytes()
2420b57cec5SDimitry Andric : MinByte - Target.minBeforeBytes();
2430b57cec5SDimitry Andric
2440b57cec5SDimitry Andric // Disregard used regions that are smaller than Offset. These are
2450b57cec5SDimitry Andric // effectively all-free regions that do not need to be checked.
2460b57cec5SDimitry Andric if (VTUsed.size() > Offset)
2470b57cec5SDimitry Andric Used.push_back(VTUsed.slice(Offset));
2480b57cec5SDimitry Andric }
2490b57cec5SDimitry Andric
2500b57cec5SDimitry Andric if (Size == 1) {
2510b57cec5SDimitry Andric // Find a free bit in each member of Used.
2520b57cec5SDimitry Andric for (unsigned I = 0;; ++I) {
2530b57cec5SDimitry Andric uint8_t BitsUsed = 0;
2540b57cec5SDimitry Andric for (auto &&B : Used)
2550b57cec5SDimitry Andric if (I < B.size())
2560b57cec5SDimitry Andric BitsUsed |= B[I];
2570b57cec5SDimitry Andric if (BitsUsed != 0xff)
25806c3fb27SDimitry Andric return (MinByte + I) * 8 + llvm::countr_zero(uint8_t(~BitsUsed));
2590b57cec5SDimitry Andric }
2600b57cec5SDimitry Andric } else {
2610b57cec5SDimitry Andric // Find a free (Size/8) byte region in each member of Used.
2620b57cec5SDimitry Andric // FIXME: see if alignment helps.
2630b57cec5SDimitry Andric for (unsigned I = 0;; ++I) {
2640b57cec5SDimitry Andric for (auto &&B : Used) {
2650b57cec5SDimitry Andric unsigned Byte = 0;
2660b57cec5SDimitry Andric while ((I + Byte) < B.size() && Byte < (Size / 8)) {
2670b57cec5SDimitry Andric if (B[I + Byte])
2680b57cec5SDimitry Andric goto NextI;
2690b57cec5SDimitry Andric ++Byte;
2700b57cec5SDimitry Andric }
2710b57cec5SDimitry Andric }
2720b57cec5SDimitry Andric return (MinByte + I) * 8;
2730b57cec5SDimitry Andric NextI:;
2740b57cec5SDimitry Andric }
2750b57cec5SDimitry Andric }
2760b57cec5SDimitry Andric }
2770b57cec5SDimitry Andric
setBeforeReturnValues(MutableArrayRef<VirtualCallTarget> Targets,uint64_t AllocBefore,unsigned BitWidth,int64_t & OffsetByte,uint64_t & OffsetBit)2780b57cec5SDimitry Andric void wholeprogramdevirt::setBeforeReturnValues(
2790b57cec5SDimitry Andric MutableArrayRef<VirtualCallTarget> Targets, uint64_t AllocBefore,
2800b57cec5SDimitry Andric unsigned BitWidth, int64_t &OffsetByte, uint64_t &OffsetBit) {
2810b57cec5SDimitry Andric if (BitWidth == 1)
2820b57cec5SDimitry Andric OffsetByte = -(AllocBefore / 8 + 1);
2830b57cec5SDimitry Andric else
2840b57cec5SDimitry Andric OffsetByte = -((AllocBefore + 7) / 8 + (BitWidth + 7) / 8);
2850b57cec5SDimitry Andric OffsetBit = AllocBefore % 8;
2860b57cec5SDimitry Andric
2870b57cec5SDimitry Andric for (VirtualCallTarget &Target : Targets) {
2880b57cec5SDimitry Andric if (BitWidth == 1)
2890b57cec5SDimitry Andric Target.setBeforeBit(AllocBefore);
2900b57cec5SDimitry Andric else
2910b57cec5SDimitry Andric Target.setBeforeBytes(AllocBefore, (BitWidth + 7) / 8);
2920b57cec5SDimitry Andric }
2930b57cec5SDimitry Andric }
2940b57cec5SDimitry Andric
setAfterReturnValues(MutableArrayRef<VirtualCallTarget> Targets,uint64_t AllocAfter,unsigned BitWidth,int64_t & OffsetByte,uint64_t & OffsetBit)2950b57cec5SDimitry Andric void wholeprogramdevirt::setAfterReturnValues(
2960b57cec5SDimitry Andric MutableArrayRef<VirtualCallTarget> Targets, uint64_t AllocAfter,
2970b57cec5SDimitry Andric unsigned BitWidth, int64_t &OffsetByte, uint64_t &OffsetBit) {
2980b57cec5SDimitry Andric if (BitWidth == 1)
2990b57cec5SDimitry Andric OffsetByte = AllocAfter / 8;
3000b57cec5SDimitry Andric else
3010b57cec5SDimitry Andric OffsetByte = (AllocAfter + 7) / 8;
3020b57cec5SDimitry Andric OffsetBit = AllocAfter % 8;
3030b57cec5SDimitry Andric
3040b57cec5SDimitry Andric for (VirtualCallTarget &Target : Targets) {
3050b57cec5SDimitry Andric if (BitWidth == 1)
3060b57cec5SDimitry Andric Target.setAfterBit(AllocAfter);
3070b57cec5SDimitry Andric else
3080b57cec5SDimitry Andric Target.setAfterBytes(AllocAfter, (BitWidth + 7) / 8);
3090b57cec5SDimitry Andric }
3100b57cec5SDimitry Andric }
3110b57cec5SDimitry Andric
VirtualCallTarget(GlobalValue * Fn,const TypeMemberInfo * TM)31206c3fb27SDimitry Andric VirtualCallTarget::VirtualCallTarget(GlobalValue *Fn, const TypeMemberInfo *TM)
3130b57cec5SDimitry Andric : Fn(Fn), TM(TM),
314*0fca6ea1SDimitry Andric IsBigEndian(Fn->getDataLayout().isBigEndian()),
31506c3fb27SDimitry Andric WasDevirt(false) {}
3160b57cec5SDimitry Andric
3170b57cec5SDimitry Andric namespace {
3180b57cec5SDimitry Andric
3190b57cec5SDimitry Andric // A slot in a set of virtual tables. The TypeID identifies the set of virtual
3200b57cec5SDimitry Andric // tables, and the ByteOffset is the offset in bytes from the address point to
3210b57cec5SDimitry Andric // the virtual function pointer.
3220b57cec5SDimitry Andric struct VTableSlot {
3230b57cec5SDimitry Andric Metadata *TypeID;
3240b57cec5SDimitry Andric uint64_t ByteOffset;
3250b57cec5SDimitry Andric };
3260b57cec5SDimitry Andric
3270b57cec5SDimitry Andric } // end anonymous namespace
3280b57cec5SDimitry Andric
3290b57cec5SDimitry Andric namespace llvm {
3300b57cec5SDimitry Andric
3310b57cec5SDimitry Andric template <> struct DenseMapInfo<VTableSlot> {
getEmptyKeyllvm::DenseMapInfo3320b57cec5SDimitry Andric static VTableSlot getEmptyKey() {
3330b57cec5SDimitry Andric return {DenseMapInfo<Metadata *>::getEmptyKey(),
3340b57cec5SDimitry Andric DenseMapInfo<uint64_t>::getEmptyKey()};
3350b57cec5SDimitry Andric }
getTombstoneKeyllvm::DenseMapInfo3360b57cec5SDimitry Andric static VTableSlot getTombstoneKey() {
3370b57cec5SDimitry Andric return {DenseMapInfo<Metadata *>::getTombstoneKey(),
3380b57cec5SDimitry Andric DenseMapInfo<uint64_t>::getTombstoneKey()};
3390b57cec5SDimitry Andric }
getHashValuellvm::DenseMapInfo3400b57cec5SDimitry Andric static unsigned getHashValue(const VTableSlot &I) {
3410b57cec5SDimitry Andric return DenseMapInfo<Metadata *>::getHashValue(I.TypeID) ^
3420b57cec5SDimitry Andric DenseMapInfo<uint64_t>::getHashValue(I.ByteOffset);
3430b57cec5SDimitry Andric }
isEqualllvm::DenseMapInfo3440b57cec5SDimitry Andric static bool isEqual(const VTableSlot &LHS,
3450b57cec5SDimitry Andric const VTableSlot &RHS) {
3460b57cec5SDimitry Andric return LHS.TypeID == RHS.TypeID && LHS.ByteOffset == RHS.ByteOffset;
3470b57cec5SDimitry Andric }
3480b57cec5SDimitry Andric };
3490b57cec5SDimitry Andric
3508bcb0991SDimitry Andric template <> struct DenseMapInfo<VTableSlotSummary> {
getEmptyKeyllvm::DenseMapInfo3518bcb0991SDimitry Andric static VTableSlotSummary getEmptyKey() {
3528bcb0991SDimitry Andric return {DenseMapInfo<StringRef>::getEmptyKey(),
3538bcb0991SDimitry Andric DenseMapInfo<uint64_t>::getEmptyKey()};
3548bcb0991SDimitry Andric }
getTombstoneKeyllvm::DenseMapInfo3558bcb0991SDimitry Andric static VTableSlotSummary getTombstoneKey() {
3568bcb0991SDimitry Andric return {DenseMapInfo<StringRef>::getTombstoneKey(),
3578bcb0991SDimitry Andric DenseMapInfo<uint64_t>::getTombstoneKey()};
3588bcb0991SDimitry Andric }
getHashValuellvm::DenseMapInfo3598bcb0991SDimitry Andric static unsigned getHashValue(const VTableSlotSummary &I) {
3608bcb0991SDimitry Andric return DenseMapInfo<StringRef>::getHashValue(I.TypeID) ^
3618bcb0991SDimitry Andric DenseMapInfo<uint64_t>::getHashValue(I.ByteOffset);
3628bcb0991SDimitry Andric }
isEqualllvm::DenseMapInfo3638bcb0991SDimitry Andric static bool isEqual(const VTableSlotSummary &LHS,
3648bcb0991SDimitry Andric const VTableSlotSummary &RHS) {
3658bcb0991SDimitry Andric return LHS.TypeID == RHS.TypeID && LHS.ByteOffset == RHS.ByteOffset;
3668bcb0991SDimitry Andric }
3678bcb0991SDimitry Andric };
3688bcb0991SDimitry Andric
3690b57cec5SDimitry Andric } // end namespace llvm
3700b57cec5SDimitry Andric
3710eae32dcSDimitry Andric // Returns true if the function must be unreachable based on ValueInfo.
3720eae32dcSDimitry Andric //
3730eae32dcSDimitry Andric // In particular, identifies a function as unreachable in the following
3740eae32dcSDimitry Andric // conditions
3750eae32dcSDimitry Andric // 1) All summaries are live.
3760eae32dcSDimitry Andric // 2) All function summaries indicate it's unreachable
37706c3fb27SDimitry Andric // 3) There is no non-function with the same GUID (which is rare)
mustBeUnreachableFunction(ValueInfo TheFnVI)3785f757f3fSDimitry Andric static bool mustBeUnreachableFunction(ValueInfo TheFnVI) {
3790eae32dcSDimitry Andric if ((!TheFnVI) || TheFnVI.getSummaryList().empty()) {
3800eae32dcSDimitry Andric // Returns false if ValueInfo is absent, or the summary list is empty
3810eae32dcSDimitry Andric // (e.g., function declarations).
3820eae32dcSDimitry Andric return false;
3830eae32dcSDimitry Andric }
3840eae32dcSDimitry Andric
385bdd1243dSDimitry Andric for (const auto &Summary : TheFnVI.getSummaryList()) {
3860eae32dcSDimitry Andric // Conservatively returns false if any non-live functions are seen.
3870eae32dcSDimitry Andric // In general either all summaries should be live or all should be dead.
3880eae32dcSDimitry Andric if (!Summary->isLive())
3890eae32dcSDimitry Andric return false;
39006c3fb27SDimitry Andric if (auto *FS = dyn_cast<FunctionSummary>(Summary->getBaseObject())) {
3910eae32dcSDimitry Andric if (!FS->fflags().MustBeUnreachable)
3920eae32dcSDimitry Andric return false;
3930eae32dcSDimitry Andric }
39406c3fb27SDimitry Andric // Be conservative if a non-function has the same GUID (which is rare).
39506c3fb27SDimitry Andric else
39606c3fb27SDimitry Andric return false;
3970eae32dcSDimitry Andric }
3980eae32dcSDimitry Andric // All function summaries are live and all of them agree that the function is
3990eae32dcSDimitry Andric // unreachble.
4000eae32dcSDimitry Andric return true;
4010eae32dcSDimitry Andric }
4020eae32dcSDimitry Andric
4035f757f3fSDimitry Andric namespace {
4040b57cec5SDimitry Andric // A virtual call site. VTable is the loaded virtual table pointer, and CS is
4050b57cec5SDimitry Andric // the indirect virtual call.
4060b57cec5SDimitry Andric struct VirtualCallSite {
4075ffd83dbSDimitry Andric Value *VTable = nullptr;
4085ffd83dbSDimitry Andric CallBase &CB;
4090b57cec5SDimitry Andric
4100b57cec5SDimitry Andric // If non-null, this field points to the associated unsafe use count stored in
4110b57cec5SDimitry Andric // the DevirtModule::NumUnsafeUsesForTypeTest map below. See the description
4120b57cec5SDimitry Andric // of that field for details.
4135ffd83dbSDimitry Andric unsigned *NumUnsafeUses = nullptr;
4140b57cec5SDimitry Andric
4150b57cec5SDimitry Andric void
emitRemark__anone706c94a0311::VirtualCallSite4160b57cec5SDimitry Andric emitRemark(const StringRef OptName, const StringRef TargetName,
4170b57cec5SDimitry Andric function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter) {
4185ffd83dbSDimitry Andric Function *F = CB.getCaller();
4195ffd83dbSDimitry Andric DebugLoc DLoc = CB.getDebugLoc();
4205ffd83dbSDimitry Andric BasicBlock *Block = CB.getParent();
4210b57cec5SDimitry Andric
4220b57cec5SDimitry Andric using namespace ore;
4230b57cec5SDimitry Andric OREGetter(F).emit(OptimizationRemark(DEBUG_TYPE, OptName, DLoc, Block)
4240b57cec5SDimitry Andric << NV("Optimization", OptName)
4250b57cec5SDimitry Andric << ": devirtualized a call to "
4260b57cec5SDimitry Andric << NV("FunctionName", TargetName));
4270b57cec5SDimitry Andric }
4280b57cec5SDimitry Andric
replaceAndErase__anone706c94a0311::VirtualCallSite4290b57cec5SDimitry Andric void replaceAndErase(
4300b57cec5SDimitry Andric const StringRef OptName, const StringRef TargetName, bool RemarksEnabled,
4310b57cec5SDimitry Andric function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter,
4320b57cec5SDimitry Andric Value *New) {
4330b57cec5SDimitry Andric if (RemarksEnabled)
4340b57cec5SDimitry Andric emitRemark(OptName, TargetName, OREGetter);
4355ffd83dbSDimitry Andric CB.replaceAllUsesWith(New);
4365ffd83dbSDimitry Andric if (auto *II = dyn_cast<InvokeInst>(&CB)) {
437*0fca6ea1SDimitry Andric BranchInst::Create(II->getNormalDest(), CB.getIterator());
4380b57cec5SDimitry Andric II->getUnwindDest()->removePredecessor(II->getParent());
4390b57cec5SDimitry Andric }
4405ffd83dbSDimitry Andric CB.eraseFromParent();
4410b57cec5SDimitry Andric // This use is no longer unsafe.
4420b57cec5SDimitry Andric if (NumUnsafeUses)
4430b57cec5SDimitry Andric --*NumUnsafeUses;
4440b57cec5SDimitry Andric }
4450b57cec5SDimitry Andric };
4460b57cec5SDimitry Andric
4470b57cec5SDimitry Andric // Call site information collected for a specific VTableSlot and possibly a list
4480b57cec5SDimitry Andric // of constant integer arguments. The grouping by arguments is handled by the
4490b57cec5SDimitry Andric // VTableSlotInfo class.
4500b57cec5SDimitry Andric struct CallSiteInfo {
4510b57cec5SDimitry Andric /// The set of call sites for this slot. Used during regular LTO and the
4520b57cec5SDimitry Andric /// import phase of ThinLTO (as well as the export phase of ThinLTO for any
4530b57cec5SDimitry Andric /// call sites that appear in the merged module itself); in each of these
4540b57cec5SDimitry Andric /// cases we are directly operating on the call sites at the IR level.
4550b57cec5SDimitry Andric std::vector<VirtualCallSite> CallSites;
4560b57cec5SDimitry Andric
4570b57cec5SDimitry Andric /// Whether all call sites represented by this CallSiteInfo, including those
4580b57cec5SDimitry Andric /// in summaries, have been devirtualized. This starts off as true because a
4590b57cec5SDimitry Andric /// default constructed CallSiteInfo represents no call sites.
4600b57cec5SDimitry Andric bool AllCallSitesDevirted = true;
4610b57cec5SDimitry Andric
4620b57cec5SDimitry Andric // These fields are used during the export phase of ThinLTO and reflect
4630b57cec5SDimitry Andric // information collected from function summaries.
4640b57cec5SDimitry Andric
4650b57cec5SDimitry Andric /// Whether any function summary contains an llvm.assume(llvm.type.test) for
4660b57cec5SDimitry Andric /// this slot.
4670b57cec5SDimitry Andric bool SummaryHasTypeTestAssumeUsers = false;
4680b57cec5SDimitry Andric
4690b57cec5SDimitry Andric /// CFI-specific: a vector containing the list of function summaries that use
4700b57cec5SDimitry Andric /// the llvm.type.checked.load intrinsic and therefore will require
4710b57cec5SDimitry Andric /// resolutions for llvm.type.test in order to implement CFI checks if
4720b57cec5SDimitry Andric /// devirtualization was unsuccessful. If devirtualization was successful, the
4730b57cec5SDimitry Andric /// pass will clear this vector by calling markDevirt(). If at the end of the
4740b57cec5SDimitry Andric /// pass the vector is non-empty, we will need to add a use of llvm.type.test
4750b57cec5SDimitry Andric /// to each of the function summaries in the vector.
4760b57cec5SDimitry Andric std::vector<FunctionSummary *> SummaryTypeCheckedLoadUsers;
4778bcb0991SDimitry Andric std::vector<FunctionSummary *> SummaryTypeTestAssumeUsers;
4780b57cec5SDimitry Andric
isExported__anone706c94a0311::CallSiteInfo4790b57cec5SDimitry Andric bool isExported() const {
4800b57cec5SDimitry Andric return SummaryHasTypeTestAssumeUsers ||
4810b57cec5SDimitry Andric !SummaryTypeCheckedLoadUsers.empty();
4820b57cec5SDimitry Andric }
4830b57cec5SDimitry Andric
addSummaryTypeCheckedLoadUser__anone706c94a0311::CallSiteInfo4848bcb0991SDimitry Andric void addSummaryTypeCheckedLoadUser(FunctionSummary *FS) {
4858bcb0991SDimitry Andric SummaryTypeCheckedLoadUsers.push_back(FS);
4860b57cec5SDimitry Andric AllCallSitesDevirted = false;
4870b57cec5SDimitry Andric }
4880b57cec5SDimitry Andric
addSummaryTypeTestAssumeUser__anone706c94a0311::CallSiteInfo4898bcb0991SDimitry Andric void addSummaryTypeTestAssumeUser(FunctionSummary *FS) {
4908bcb0991SDimitry Andric SummaryTypeTestAssumeUsers.push_back(FS);
4918bcb0991SDimitry Andric SummaryHasTypeTestAssumeUsers = true;
4920b57cec5SDimitry Andric AllCallSitesDevirted = false;
4930b57cec5SDimitry Andric }
4940b57cec5SDimitry Andric
markDevirt__anone706c94a0311::CallSiteInfo4950b57cec5SDimitry Andric void markDevirt() {
4960b57cec5SDimitry Andric AllCallSitesDevirted = true;
4970b57cec5SDimitry Andric
4980b57cec5SDimitry Andric // As explained in the comment for SummaryTypeCheckedLoadUsers.
4990b57cec5SDimitry Andric SummaryTypeCheckedLoadUsers.clear();
5000b57cec5SDimitry Andric }
5010b57cec5SDimitry Andric };
5020b57cec5SDimitry Andric
5030b57cec5SDimitry Andric // Call site information collected for a specific VTableSlot.
5040b57cec5SDimitry Andric struct VTableSlotInfo {
5050b57cec5SDimitry Andric // The set of call sites which do not have all constant integer arguments
5060b57cec5SDimitry Andric // (excluding "this").
5070b57cec5SDimitry Andric CallSiteInfo CSInfo;
5080b57cec5SDimitry Andric
5090b57cec5SDimitry Andric // The set of call sites with all constant integer arguments (excluding
5100b57cec5SDimitry Andric // "this"), grouped by argument list.
5110b57cec5SDimitry Andric std::map<std::vector<uint64_t>, CallSiteInfo> ConstCSInfo;
5120b57cec5SDimitry Andric
5135ffd83dbSDimitry Andric void addCallSite(Value *VTable, CallBase &CB, unsigned *NumUnsafeUses);
5140b57cec5SDimitry Andric
5150b57cec5SDimitry Andric private:
5165ffd83dbSDimitry Andric CallSiteInfo &findCallSiteInfo(CallBase &CB);
5170b57cec5SDimitry Andric };
5180b57cec5SDimitry Andric
findCallSiteInfo(CallBase & CB)5195ffd83dbSDimitry Andric CallSiteInfo &VTableSlotInfo::findCallSiteInfo(CallBase &CB) {
5200b57cec5SDimitry Andric std::vector<uint64_t> Args;
5215ffd83dbSDimitry Andric auto *CBType = dyn_cast<IntegerType>(CB.getType());
5225ffd83dbSDimitry Andric if (!CBType || CBType->getBitWidth() > 64 || CB.arg_empty())
5230b57cec5SDimitry Andric return CSInfo;
524e8d8bef9SDimitry Andric for (auto &&Arg : drop_begin(CB.args())) {
5250b57cec5SDimitry Andric auto *CI = dyn_cast<ConstantInt>(Arg);
5260b57cec5SDimitry Andric if (!CI || CI->getBitWidth() > 64)
5270b57cec5SDimitry Andric return CSInfo;
5280b57cec5SDimitry Andric Args.push_back(CI->getZExtValue());
5290b57cec5SDimitry Andric }
5300b57cec5SDimitry Andric return ConstCSInfo[Args];
5310b57cec5SDimitry Andric }
5320b57cec5SDimitry Andric
addCallSite(Value * VTable,CallBase & CB,unsigned * NumUnsafeUses)5335ffd83dbSDimitry Andric void VTableSlotInfo::addCallSite(Value *VTable, CallBase &CB,
5340b57cec5SDimitry Andric unsigned *NumUnsafeUses) {
5355ffd83dbSDimitry Andric auto &CSI = findCallSiteInfo(CB);
5360b57cec5SDimitry Andric CSI.AllCallSitesDevirted = false;
5375ffd83dbSDimitry Andric CSI.CallSites.push_back({VTable, CB, NumUnsafeUses});
5380b57cec5SDimitry Andric }
5390b57cec5SDimitry Andric
5400b57cec5SDimitry Andric struct DevirtModule {
5410b57cec5SDimitry Andric Module &M;
5420b57cec5SDimitry Andric function_ref<AAResults &(Function &)> AARGetter;
5430b57cec5SDimitry Andric function_ref<DominatorTree &(Function &)> LookupDomTree;
5440b57cec5SDimitry Andric
5450b57cec5SDimitry Andric ModuleSummaryIndex *ExportSummary;
5460b57cec5SDimitry Andric const ModuleSummaryIndex *ImportSummary;
5470b57cec5SDimitry Andric
5480b57cec5SDimitry Andric IntegerType *Int8Ty;
5490b57cec5SDimitry Andric PointerType *Int8PtrTy;
5500b57cec5SDimitry Andric IntegerType *Int32Ty;
5510b57cec5SDimitry Andric IntegerType *Int64Ty;
5520b57cec5SDimitry Andric IntegerType *IntPtrTy;
5535ffd83dbSDimitry Andric /// Sizeless array type, used for imported vtables. This provides a signal
5545ffd83dbSDimitry Andric /// to analyzers that these imports may alias, as they do for example
5555ffd83dbSDimitry Andric /// when multiple unique return values occur in the same vtable.
5565ffd83dbSDimitry Andric ArrayType *Int8Arr0Ty;
5570b57cec5SDimitry Andric
5580b57cec5SDimitry Andric bool RemarksEnabled;
5590b57cec5SDimitry Andric function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter;
5600b57cec5SDimitry Andric
5610b57cec5SDimitry Andric MapVector<VTableSlot, VTableSlotInfo> CallSlots;
5620b57cec5SDimitry Andric
563fe6060f1SDimitry Andric // Calls that have already been optimized. We may add a call to multiple
564fe6060f1SDimitry Andric // VTableSlotInfos if vtable loads are coalesced and need to make sure not to
565fe6060f1SDimitry Andric // optimize a call more than once.
566fe6060f1SDimitry Andric SmallPtrSet<CallBase *, 8> OptimizedCalls;
567fe6060f1SDimitry Andric
56806c3fb27SDimitry Andric // Store calls that had their ptrauth bundle removed. They are to be deleted
56906c3fb27SDimitry Andric // at the end of the optimization.
57006c3fb27SDimitry Andric SmallVector<CallBase *, 8> CallsWithPtrAuthBundleRemoved;
57106c3fb27SDimitry Andric
5720b57cec5SDimitry Andric // This map keeps track of the number of "unsafe" uses of a loaded function
5730b57cec5SDimitry Andric // pointer. The key is the associated llvm.type.test intrinsic call generated
5740b57cec5SDimitry Andric // by this pass. An unsafe use is one that calls the loaded function pointer
5750b57cec5SDimitry Andric // directly. Every time we eliminate an unsafe use (for example, by
5760b57cec5SDimitry Andric // devirtualizing it or by applying virtual constant propagation), we
5770b57cec5SDimitry Andric // decrement the value stored in this map. If a value reaches zero, we can
5780b57cec5SDimitry Andric // eliminate the type check by RAUWing the associated llvm.type.test call with
5790b57cec5SDimitry Andric // true.
5800b57cec5SDimitry Andric std::map<CallInst *, unsigned> NumUnsafeUsesForTypeTest;
5815ffd83dbSDimitry Andric PatternList FunctionsToSkip;
5820b57cec5SDimitry Andric
DevirtModule__anone706c94a0311::DevirtModule5830b57cec5SDimitry Andric DevirtModule(Module &M, function_ref<AAResults &(Function &)> AARGetter,
5840b57cec5SDimitry Andric function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter,
5850b57cec5SDimitry Andric function_ref<DominatorTree &(Function &)> LookupDomTree,
5860b57cec5SDimitry Andric ModuleSummaryIndex *ExportSummary,
5870b57cec5SDimitry Andric const ModuleSummaryIndex *ImportSummary)
5880b57cec5SDimitry Andric : M(M), AARGetter(AARGetter), LookupDomTree(LookupDomTree),
5890b57cec5SDimitry Andric ExportSummary(ExportSummary), ImportSummary(ImportSummary),
5900b57cec5SDimitry Andric Int8Ty(Type::getInt8Ty(M.getContext())),
5915f757f3fSDimitry Andric Int8PtrTy(PointerType::getUnqual(M.getContext())),
5920b57cec5SDimitry Andric Int32Ty(Type::getInt32Ty(M.getContext())),
5930b57cec5SDimitry Andric Int64Ty(Type::getInt64Ty(M.getContext())),
5940b57cec5SDimitry Andric IntPtrTy(M.getDataLayout().getIntPtrType(M.getContext(), 0)),
5955ffd83dbSDimitry Andric Int8Arr0Ty(ArrayType::get(Type::getInt8Ty(M.getContext()), 0)),
5960b57cec5SDimitry Andric RemarksEnabled(areRemarksEnabled()), OREGetter(OREGetter) {
5970b57cec5SDimitry Andric assert(!(ExportSummary && ImportSummary));
5985ffd83dbSDimitry Andric FunctionsToSkip.init(SkipFunctionNames);
5990b57cec5SDimitry Andric }
6000b57cec5SDimitry Andric
6010b57cec5SDimitry Andric bool areRemarksEnabled();
6020b57cec5SDimitry Andric
6035ffd83dbSDimitry Andric void
6045ffd83dbSDimitry Andric scanTypeTestUsers(Function *TypeTestFunc,
6055ffd83dbSDimitry Andric DenseMap<Metadata *, std::set<TypeMemberInfo>> &TypeIdMap);
6060b57cec5SDimitry Andric void scanTypeCheckedLoadUsers(Function *TypeCheckedLoadFunc);
6070b57cec5SDimitry Andric
6080b57cec5SDimitry Andric void buildTypeIdentifierMap(
6090b57cec5SDimitry Andric std::vector<VTableBits> &Bits,
6100b57cec5SDimitry Andric DenseMap<Metadata *, std::set<TypeMemberInfo>> &TypeIdMap);
6110eae32dcSDimitry Andric
6120b57cec5SDimitry Andric bool
6130b57cec5SDimitry Andric tryFindVirtualCallTargets(std::vector<VirtualCallTarget> &TargetsForSlot,
6140b57cec5SDimitry Andric const std::set<TypeMemberInfo> &TypeMemberInfos,
6150eae32dcSDimitry Andric uint64_t ByteOffset,
6160eae32dcSDimitry Andric ModuleSummaryIndex *ExportSummary);
6170b57cec5SDimitry Andric
6180b57cec5SDimitry Andric void applySingleImplDevirt(VTableSlotInfo &SlotInfo, Constant *TheFn,
6190b57cec5SDimitry Andric bool &IsExported);
6208bcb0991SDimitry Andric bool trySingleImplDevirt(ModuleSummaryIndex *ExportSummary,
6218bcb0991SDimitry Andric MutableArrayRef<VirtualCallTarget> TargetsForSlot,
6220b57cec5SDimitry Andric VTableSlotInfo &SlotInfo,
6230b57cec5SDimitry Andric WholeProgramDevirtResolution *Res);
6240b57cec5SDimitry Andric
6250b57cec5SDimitry Andric void applyICallBranchFunnel(VTableSlotInfo &SlotInfo, Constant *JT,
6260b57cec5SDimitry Andric bool &IsExported);
6270b57cec5SDimitry Andric void tryICallBranchFunnel(MutableArrayRef<VirtualCallTarget> TargetsForSlot,
6280b57cec5SDimitry Andric VTableSlotInfo &SlotInfo,
6290b57cec5SDimitry Andric WholeProgramDevirtResolution *Res, VTableSlot Slot);
6300b57cec5SDimitry Andric
6310b57cec5SDimitry Andric bool tryEvaluateFunctionsWithArgs(
6320b57cec5SDimitry Andric MutableArrayRef<VirtualCallTarget> TargetsForSlot,
6330b57cec5SDimitry Andric ArrayRef<uint64_t> Args);
6340b57cec5SDimitry Andric
6350b57cec5SDimitry Andric void applyUniformRetValOpt(CallSiteInfo &CSInfo, StringRef FnName,
6360b57cec5SDimitry Andric uint64_t TheRetVal);
6370b57cec5SDimitry Andric bool tryUniformRetValOpt(MutableArrayRef<VirtualCallTarget> TargetsForSlot,
6380b57cec5SDimitry Andric CallSiteInfo &CSInfo,
6390b57cec5SDimitry Andric WholeProgramDevirtResolution::ByArg *Res);
6400b57cec5SDimitry Andric
6410b57cec5SDimitry Andric // Returns the global symbol name that is used to export information about the
6420b57cec5SDimitry Andric // given vtable slot and list of arguments.
6430b57cec5SDimitry Andric std::string getGlobalName(VTableSlot Slot, ArrayRef<uint64_t> Args,
6440b57cec5SDimitry Andric StringRef Name);
6450b57cec5SDimitry Andric
6460b57cec5SDimitry Andric bool shouldExportConstantsAsAbsoluteSymbols();
6470b57cec5SDimitry Andric
6480b57cec5SDimitry Andric // This function is called during the export phase to create a symbol
6490b57cec5SDimitry Andric // definition containing information about the given vtable slot and list of
6500b57cec5SDimitry Andric // arguments.
6510b57cec5SDimitry Andric void exportGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args, StringRef Name,
6520b57cec5SDimitry Andric Constant *C);
6530b57cec5SDimitry Andric void exportConstant(VTableSlot Slot, ArrayRef<uint64_t> Args, StringRef Name,
6540b57cec5SDimitry Andric uint32_t Const, uint32_t &Storage);
6550b57cec5SDimitry Andric
6560b57cec5SDimitry Andric // This function is called during the import phase to create a reference to
6570b57cec5SDimitry Andric // the symbol definition created during the export phase.
6580b57cec5SDimitry Andric Constant *importGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args,
6590b57cec5SDimitry Andric StringRef Name);
6600b57cec5SDimitry Andric Constant *importConstant(VTableSlot Slot, ArrayRef<uint64_t> Args,
6610b57cec5SDimitry Andric StringRef Name, IntegerType *IntTy,
6620b57cec5SDimitry Andric uint32_t Storage);
6630b57cec5SDimitry Andric
6640b57cec5SDimitry Andric Constant *getMemberAddr(const TypeMemberInfo *M);
6650b57cec5SDimitry Andric
6660b57cec5SDimitry Andric void applyUniqueRetValOpt(CallSiteInfo &CSInfo, StringRef FnName, bool IsOne,
6670b57cec5SDimitry Andric Constant *UniqueMemberAddr);
6680b57cec5SDimitry Andric bool tryUniqueRetValOpt(unsigned BitWidth,
6690b57cec5SDimitry Andric MutableArrayRef<VirtualCallTarget> TargetsForSlot,
6700b57cec5SDimitry Andric CallSiteInfo &CSInfo,
6710b57cec5SDimitry Andric WholeProgramDevirtResolution::ByArg *Res,
6720b57cec5SDimitry Andric VTableSlot Slot, ArrayRef<uint64_t> Args);
6730b57cec5SDimitry Andric
6740b57cec5SDimitry Andric void applyVirtualConstProp(CallSiteInfo &CSInfo, StringRef FnName,
6750b57cec5SDimitry Andric Constant *Byte, Constant *Bit);
6760b57cec5SDimitry Andric bool tryVirtualConstProp(MutableArrayRef<VirtualCallTarget> TargetsForSlot,
6770b57cec5SDimitry Andric VTableSlotInfo &SlotInfo,
6780b57cec5SDimitry Andric WholeProgramDevirtResolution *Res, VTableSlot Slot);
6790b57cec5SDimitry Andric
6800b57cec5SDimitry Andric void rebuildGlobal(VTableBits &B);
6810b57cec5SDimitry Andric
6820b57cec5SDimitry Andric // Apply the summary resolution for Slot to all virtual calls in SlotInfo.
6830b57cec5SDimitry Andric void importResolution(VTableSlot Slot, VTableSlotInfo &SlotInfo);
6840b57cec5SDimitry Andric
6850b57cec5SDimitry Andric // If we were able to eliminate all unsafe uses for a type checked load,
6860b57cec5SDimitry Andric // eliminate the associated type tests by replacing them with true.
6870b57cec5SDimitry Andric void removeRedundantTypeTests();
6880b57cec5SDimitry Andric
6890b57cec5SDimitry Andric bool run();
6900b57cec5SDimitry Andric
6910eae32dcSDimitry Andric // Look up the corresponding ValueInfo entry of `TheFn` in `ExportSummary`.
6920eae32dcSDimitry Andric //
6930eae32dcSDimitry Andric // Caller guarantees that `ExportSummary` is not nullptr.
6940eae32dcSDimitry Andric static ValueInfo lookUpFunctionValueInfo(Function *TheFn,
6950eae32dcSDimitry Andric ModuleSummaryIndex *ExportSummary);
6960eae32dcSDimitry Andric
6970eae32dcSDimitry Andric // Returns true if the function definition must be unreachable.
6980eae32dcSDimitry Andric //
6990eae32dcSDimitry Andric // Note if this helper function returns true, `F` is guaranteed
7000eae32dcSDimitry Andric // to be unreachable; if it returns false, `F` might still
7010eae32dcSDimitry Andric // be unreachable but not covered by this helper function.
7020eae32dcSDimitry Andric //
7030eae32dcSDimitry Andric // Implementation-wise, if function definition is present, IR is analyzed; if
7040eae32dcSDimitry Andric // not, look up function flags from ExportSummary as a fallback.
7050eae32dcSDimitry Andric static bool mustBeUnreachableFunction(Function *const F,
7060eae32dcSDimitry Andric ModuleSummaryIndex *ExportSummary);
7070eae32dcSDimitry Andric
7080b57cec5SDimitry Andric // Lower the module using the action and summary passed as command line
7090b57cec5SDimitry Andric // arguments. For testing purposes only.
7100b57cec5SDimitry Andric static bool
7110b57cec5SDimitry Andric runForTesting(Module &M, function_ref<AAResults &(Function &)> AARGetter,
7120b57cec5SDimitry Andric function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter,
7130b57cec5SDimitry Andric function_ref<DominatorTree &(Function &)> LookupDomTree);
7140b57cec5SDimitry Andric };
7150b57cec5SDimitry Andric
7168bcb0991SDimitry Andric struct DevirtIndex {
7178bcb0991SDimitry Andric ModuleSummaryIndex &ExportSummary;
7188bcb0991SDimitry Andric // The set in which to record GUIDs exported from their module by
7198bcb0991SDimitry Andric // devirtualization, used by client to ensure they are not internalized.
7208bcb0991SDimitry Andric std::set<GlobalValue::GUID> &ExportedGUIDs;
7218bcb0991SDimitry Andric // A map in which to record the information necessary to locate the WPD
7228bcb0991SDimitry Andric // resolution for local targets in case they are exported by cross module
7238bcb0991SDimitry Andric // importing.
7248bcb0991SDimitry Andric std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap;
7258bcb0991SDimitry Andric
7268bcb0991SDimitry Andric MapVector<VTableSlotSummary, VTableSlotInfo> CallSlots;
7278bcb0991SDimitry Andric
7285ffd83dbSDimitry Andric PatternList FunctionsToSkip;
7295ffd83dbSDimitry Andric
DevirtIndex__anone706c94a0311::DevirtIndex7308bcb0991SDimitry Andric DevirtIndex(
7318bcb0991SDimitry Andric ModuleSummaryIndex &ExportSummary,
7328bcb0991SDimitry Andric std::set<GlobalValue::GUID> &ExportedGUIDs,
7338bcb0991SDimitry Andric std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap)
7348bcb0991SDimitry Andric : ExportSummary(ExportSummary), ExportedGUIDs(ExportedGUIDs),
7355ffd83dbSDimitry Andric LocalWPDTargetsMap(LocalWPDTargetsMap) {
7365ffd83dbSDimitry Andric FunctionsToSkip.init(SkipFunctionNames);
7375ffd83dbSDimitry Andric }
7388bcb0991SDimitry Andric
7398bcb0991SDimitry Andric bool tryFindVirtualCallTargets(std::vector<ValueInfo> &TargetsForSlot,
7408bcb0991SDimitry Andric const TypeIdCompatibleVtableInfo TIdInfo,
7418bcb0991SDimitry Andric uint64_t ByteOffset);
7428bcb0991SDimitry Andric
7438bcb0991SDimitry Andric bool trySingleImplDevirt(MutableArrayRef<ValueInfo> TargetsForSlot,
7448bcb0991SDimitry Andric VTableSlotSummary &SlotSummary,
7458bcb0991SDimitry Andric VTableSlotInfo &SlotInfo,
7468bcb0991SDimitry Andric WholeProgramDevirtResolution *Res,
7478bcb0991SDimitry Andric std::set<ValueInfo> &DevirtTargets);
7488bcb0991SDimitry Andric
7498bcb0991SDimitry Andric void run();
7508bcb0991SDimitry Andric };
7510b57cec5SDimitry Andric } // end anonymous namespace
7520b57cec5SDimitry Andric
run(Module & M,ModuleAnalysisManager & AM)7530b57cec5SDimitry Andric PreservedAnalyses WholeProgramDevirtPass::run(Module &M,
7540b57cec5SDimitry Andric ModuleAnalysisManager &AM) {
7550b57cec5SDimitry Andric auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
7560b57cec5SDimitry Andric auto AARGetter = [&](Function &F) -> AAResults & {
7570b57cec5SDimitry Andric return FAM.getResult<AAManager>(F);
7580b57cec5SDimitry Andric };
7590b57cec5SDimitry Andric auto OREGetter = [&](Function *F) -> OptimizationRemarkEmitter & {
7600b57cec5SDimitry Andric return FAM.getResult<OptimizationRemarkEmitterAnalysis>(*F);
7610b57cec5SDimitry Andric };
7620b57cec5SDimitry Andric auto LookupDomTree = [&FAM](Function &F) -> DominatorTree & {
7630b57cec5SDimitry Andric return FAM.getResult<DominatorTreeAnalysis>(F);
7640b57cec5SDimitry Andric };
765e8d8bef9SDimitry Andric if (UseCommandLine) {
76606c3fb27SDimitry Andric if (!DevirtModule::runForTesting(M, AARGetter, OREGetter, LookupDomTree))
767e8d8bef9SDimitry Andric return PreservedAnalyses::all();
768e8d8bef9SDimitry Andric return PreservedAnalyses::none();
769e8d8bef9SDimitry Andric }
7700b57cec5SDimitry Andric if (!DevirtModule(M, AARGetter, OREGetter, LookupDomTree, ExportSummary,
7710b57cec5SDimitry Andric ImportSummary)
7720b57cec5SDimitry Andric .run())
7730b57cec5SDimitry Andric return PreservedAnalyses::all();
7740b57cec5SDimitry Andric return PreservedAnalyses::none();
7750b57cec5SDimitry Andric }
7760b57cec5SDimitry Andric
7775ffd83dbSDimitry Andric // Enable whole program visibility if enabled by client (e.g. linker) or
7785ffd83dbSDimitry Andric // internal option, and not force disabled.
hasWholeProgramVisibility(bool WholeProgramVisibilityEnabledInLTO)7795f757f3fSDimitry Andric bool llvm::hasWholeProgramVisibility(bool WholeProgramVisibilityEnabledInLTO) {
7805ffd83dbSDimitry Andric return (WholeProgramVisibilityEnabledInLTO || WholeProgramVisibility) &&
7815ffd83dbSDimitry Andric !DisableWholeProgramVisibility;
7825ffd83dbSDimitry Andric }
7835ffd83dbSDimitry Andric
7845f757f3fSDimitry Andric static bool
typeIDVisibleToRegularObj(StringRef TypeID,function_ref<bool (StringRef)> IsVisibleToRegularObj)7855f757f3fSDimitry Andric typeIDVisibleToRegularObj(StringRef TypeID,
7865f757f3fSDimitry Andric function_ref<bool(StringRef)> IsVisibleToRegularObj) {
7875f757f3fSDimitry Andric // TypeID for member function pointer type is an internal construct
7885f757f3fSDimitry Andric // and won't exist in IsVisibleToRegularObj. The full TypeID
7895f757f3fSDimitry Andric // will be present and participate in invalidation.
7905f757f3fSDimitry Andric if (TypeID.ends_with(".virtual"))
7915f757f3fSDimitry Andric return false;
7925f757f3fSDimitry Andric
7935f757f3fSDimitry Andric // TypeID that doesn't start with Itanium mangling (_ZTS) will be
7945f757f3fSDimitry Andric // non-externally visible types which cannot interact with
7955f757f3fSDimitry Andric // external native files. See CodeGenModule::CreateMetadataIdentifierImpl.
7965f757f3fSDimitry Andric if (!TypeID.consume_front("_ZTS"))
7975f757f3fSDimitry Andric return false;
7985f757f3fSDimitry Andric
7995f757f3fSDimitry Andric // TypeID is keyed off the type name symbol (_ZTS). However, the native
8005f757f3fSDimitry Andric // object may not contain this symbol if it does not contain a key
8015f757f3fSDimitry Andric // function for the base type and thus only contains a reference to the
8025f757f3fSDimitry Andric // type info (_ZTI). To catch this case we query using the type info
8035f757f3fSDimitry Andric // symbol corresponding to the TypeID.
8045f757f3fSDimitry Andric std::string typeInfo = ("_ZTI" + TypeID).str();
8055f757f3fSDimitry Andric return IsVisibleToRegularObj(typeInfo);
8065f757f3fSDimitry Andric }
8075f757f3fSDimitry Andric
8085f757f3fSDimitry Andric static bool
skipUpdateDueToValidation(GlobalVariable & GV,function_ref<bool (StringRef)> IsVisibleToRegularObj)8095f757f3fSDimitry Andric skipUpdateDueToValidation(GlobalVariable &GV,
8105f757f3fSDimitry Andric function_ref<bool(StringRef)> IsVisibleToRegularObj) {
8115f757f3fSDimitry Andric SmallVector<MDNode *, 2> Types;
8125f757f3fSDimitry Andric GV.getMetadata(LLVMContext::MD_type, Types);
8135f757f3fSDimitry Andric
8145f757f3fSDimitry Andric for (auto Type : Types)
8155f757f3fSDimitry Andric if (auto *TypeID = dyn_cast<MDString>(Type->getOperand(1).get()))
8165f757f3fSDimitry Andric return typeIDVisibleToRegularObj(TypeID->getString(),
8175f757f3fSDimitry Andric IsVisibleToRegularObj);
8185f757f3fSDimitry Andric
8195f757f3fSDimitry Andric return false;
8205f757f3fSDimitry Andric }
8215f757f3fSDimitry Andric
8225ffd83dbSDimitry Andric /// If whole program visibility asserted, then upgrade all public vcall
8235ffd83dbSDimitry Andric /// visibility metadata on vtable definitions to linkage unit visibility in
8245ffd83dbSDimitry Andric /// Module IR (for regular or hybrid LTO).
updateVCallVisibilityInModule(Module & M,bool WholeProgramVisibilityEnabledInLTO,const DenseSet<GlobalValue::GUID> & DynamicExportSymbols,bool ValidateAllVtablesHaveTypeInfos,function_ref<bool (StringRef)> IsVisibleToRegularObj)8255f757f3fSDimitry Andric void llvm::updateVCallVisibilityInModule(
826fe6060f1SDimitry Andric Module &M, bool WholeProgramVisibilityEnabledInLTO,
8275f757f3fSDimitry Andric const DenseSet<GlobalValue::GUID> &DynamicExportSymbols,
8285f757f3fSDimitry Andric bool ValidateAllVtablesHaveTypeInfos,
8295f757f3fSDimitry Andric function_ref<bool(StringRef)> IsVisibleToRegularObj) {
8305ffd83dbSDimitry Andric if (!hasWholeProgramVisibility(WholeProgramVisibilityEnabledInLTO))
8315ffd83dbSDimitry Andric return;
832972a253aSDimitry Andric for (GlobalVariable &GV : M.globals()) {
8335ffd83dbSDimitry Andric // Add linkage unit visibility to any variable with type metadata, which are
8345ffd83dbSDimitry Andric // the vtable definitions. We won't have an existing vcall_visibility
8355ffd83dbSDimitry Andric // metadata on vtable definitions with public visibility.
8365ffd83dbSDimitry Andric if (GV.hasMetadata(LLVMContext::MD_type) &&
837fe6060f1SDimitry Andric GV.getVCallVisibility() == GlobalObject::VCallVisibilityPublic &&
838fe6060f1SDimitry Andric // Don't upgrade the visibility for symbols exported to the dynamic
839fe6060f1SDimitry Andric // linker, as we have no information on their eventual use.
8405f757f3fSDimitry Andric !DynamicExportSymbols.count(GV.getGUID()) &&
8415f757f3fSDimitry Andric // With validation enabled, we want to exclude symbols visible to
8425f757f3fSDimitry Andric // regular objects. Local symbols will be in this group due to the
8435f757f3fSDimitry Andric // current implementation but those with VCallVisibilityTranslationUnit
8445f757f3fSDimitry Andric // will have already been marked in clang so are unaffected.
8455f757f3fSDimitry Andric !(ValidateAllVtablesHaveTypeInfos &&
8465f757f3fSDimitry Andric skipUpdateDueToValidation(GV, IsVisibleToRegularObj)))
8475ffd83dbSDimitry Andric GV.setVCallVisibilityMetadata(GlobalObject::VCallVisibilityLinkageUnit);
8485ffd83dbSDimitry Andric }
849972a253aSDimitry Andric }
850972a253aSDimitry Andric
updatePublicTypeTestCalls(Module & M,bool WholeProgramVisibilityEnabledInLTO)8515f757f3fSDimitry Andric void llvm::updatePublicTypeTestCalls(Module &M,
852972a253aSDimitry Andric bool WholeProgramVisibilityEnabledInLTO) {
853972a253aSDimitry Andric Function *PublicTypeTestFunc =
854972a253aSDimitry Andric M.getFunction(Intrinsic::getName(Intrinsic::public_type_test));
855972a253aSDimitry Andric if (!PublicTypeTestFunc)
856972a253aSDimitry Andric return;
857972a253aSDimitry Andric if (hasWholeProgramVisibility(WholeProgramVisibilityEnabledInLTO)) {
858972a253aSDimitry Andric Function *TypeTestFunc =
859972a253aSDimitry Andric Intrinsic::getDeclaration(&M, Intrinsic::type_test);
860972a253aSDimitry Andric for (Use &U : make_early_inc_range(PublicTypeTestFunc->uses())) {
861972a253aSDimitry Andric auto *CI = cast<CallInst>(U.getUser());
862972a253aSDimitry Andric auto *NewCI = CallInst::Create(
863bdd1243dSDimitry Andric TypeTestFunc, {CI->getArgOperand(0), CI->getArgOperand(1)},
864*0fca6ea1SDimitry Andric std::nullopt, "", CI->getIterator());
865972a253aSDimitry Andric CI->replaceAllUsesWith(NewCI);
866972a253aSDimitry Andric CI->eraseFromParent();
867972a253aSDimitry Andric }
868972a253aSDimitry Andric } else {
869972a253aSDimitry Andric auto *True = ConstantInt::getTrue(M.getContext());
870972a253aSDimitry Andric for (Use &U : make_early_inc_range(PublicTypeTestFunc->uses())) {
871972a253aSDimitry Andric auto *CI = cast<CallInst>(U.getUser());
872972a253aSDimitry Andric CI->replaceAllUsesWith(True);
873972a253aSDimitry Andric CI->eraseFromParent();
874972a253aSDimitry Andric }
875972a253aSDimitry Andric }
876972a253aSDimitry Andric }
8775ffd83dbSDimitry Andric
8785f757f3fSDimitry Andric /// Based on typeID string, get all associated vtable GUIDS that are
8795f757f3fSDimitry Andric /// visible to regular objects.
getVisibleToRegularObjVtableGUIDs(ModuleSummaryIndex & Index,DenseSet<GlobalValue::GUID> & VisibleToRegularObjSymbols,function_ref<bool (StringRef)> IsVisibleToRegularObj)8805f757f3fSDimitry Andric void llvm::getVisibleToRegularObjVtableGUIDs(
8815f757f3fSDimitry Andric ModuleSummaryIndex &Index,
8825f757f3fSDimitry Andric DenseSet<GlobalValue::GUID> &VisibleToRegularObjSymbols,
8835f757f3fSDimitry Andric function_ref<bool(StringRef)> IsVisibleToRegularObj) {
8845f757f3fSDimitry Andric for (const auto &typeID : Index.typeIdCompatibleVtableMap()) {
8855f757f3fSDimitry Andric if (typeIDVisibleToRegularObj(typeID.first, IsVisibleToRegularObj))
8865f757f3fSDimitry Andric for (const TypeIdOffsetVtableInfo &P : typeID.second)
8875f757f3fSDimitry Andric VisibleToRegularObjSymbols.insert(P.VTableVI.getGUID());
8885f757f3fSDimitry Andric }
8895f757f3fSDimitry Andric }
8905f757f3fSDimitry Andric
8915ffd83dbSDimitry Andric /// If whole program visibility asserted, then upgrade all public vcall
8925ffd83dbSDimitry Andric /// visibility metadata on vtable definition summaries to linkage unit
8935ffd83dbSDimitry Andric /// visibility in Module summary index (for ThinLTO).
updateVCallVisibilityInIndex(ModuleSummaryIndex & Index,bool WholeProgramVisibilityEnabledInLTO,const DenseSet<GlobalValue::GUID> & DynamicExportSymbols,const DenseSet<GlobalValue::GUID> & VisibleToRegularObjSymbols)8945f757f3fSDimitry Andric void llvm::updateVCallVisibilityInIndex(
895fe6060f1SDimitry Andric ModuleSummaryIndex &Index, bool WholeProgramVisibilityEnabledInLTO,
8965f757f3fSDimitry Andric const DenseSet<GlobalValue::GUID> &DynamicExportSymbols,
8975f757f3fSDimitry Andric const DenseSet<GlobalValue::GUID> &VisibleToRegularObjSymbols) {
8985ffd83dbSDimitry Andric if (!hasWholeProgramVisibility(WholeProgramVisibilityEnabledInLTO))
8995ffd83dbSDimitry Andric return;
9005ffd83dbSDimitry Andric for (auto &P : Index) {
90181ad6265SDimitry Andric // Don't upgrade the visibility for symbols exported to the dynamic
90281ad6265SDimitry Andric // linker, as we have no information on their eventual use.
90381ad6265SDimitry Andric if (DynamicExportSymbols.count(P.first))
90481ad6265SDimitry Andric continue;
9055ffd83dbSDimitry Andric for (auto &S : P.second.SummaryList) {
9065ffd83dbSDimitry Andric auto *GVar = dyn_cast<GlobalVarSummary>(S.get());
907fe6060f1SDimitry Andric if (!GVar ||
90881ad6265SDimitry Andric GVar->getVCallVisibility() != GlobalObject::VCallVisibilityPublic)
9095ffd83dbSDimitry Andric continue;
9105f757f3fSDimitry Andric // With validation enabled, we want to exclude symbols visible to regular
9115f757f3fSDimitry Andric // objects. Local symbols will be in this group due to the current
9125f757f3fSDimitry Andric // implementation but those with VCallVisibilityTranslationUnit will have
9135f757f3fSDimitry Andric // already been marked in clang so are unaffected.
9145f757f3fSDimitry Andric if (VisibleToRegularObjSymbols.count(P.first))
9155f757f3fSDimitry Andric continue;
9165ffd83dbSDimitry Andric GVar->setVCallVisibility(GlobalObject::VCallVisibilityLinkageUnit);
9175ffd83dbSDimitry Andric }
9185ffd83dbSDimitry Andric }
9195ffd83dbSDimitry Andric }
9205ffd83dbSDimitry Andric
runWholeProgramDevirtOnIndex(ModuleSummaryIndex & Summary,std::set<GlobalValue::GUID> & ExportedGUIDs,std::map<ValueInfo,std::vector<VTableSlotSummary>> & LocalWPDTargetsMap)9215f757f3fSDimitry Andric void llvm::runWholeProgramDevirtOnIndex(
9228bcb0991SDimitry Andric ModuleSummaryIndex &Summary, std::set<GlobalValue::GUID> &ExportedGUIDs,
9238bcb0991SDimitry Andric std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap) {
9248bcb0991SDimitry Andric DevirtIndex(Summary, ExportedGUIDs, LocalWPDTargetsMap).run();
9258bcb0991SDimitry Andric }
9268bcb0991SDimitry Andric
updateIndexWPDForExports(ModuleSummaryIndex & Summary,function_ref<bool (StringRef,ValueInfo)> isExported,std::map<ValueInfo,std::vector<VTableSlotSummary>> & LocalWPDTargetsMap)9275f757f3fSDimitry Andric void llvm::updateIndexWPDForExports(
9288bcb0991SDimitry Andric ModuleSummaryIndex &Summary,
929480093f4SDimitry Andric function_ref<bool(StringRef, ValueInfo)> isExported,
9308bcb0991SDimitry Andric std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap) {
9318bcb0991SDimitry Andric for (auto &T : LocalWPDTargetsMap) {
9328bcb0991SDimitry Andric auto &VI = T.first;
9338bcb0991SDimitry Andric // This was enforced earlier during trySingleImplDevirt.
9348bcb0991SDimitry Andric assert(VI.getSummaryList().size() == 1 &&
9358bcb0991SDimitry Andric "Devirt of local target has more than one copy");
9368bcb0991SDimitry Andric auto &S = VI.getSummaryList()[0];
937480093f4SDimitry Andric if (!isExported(S->modulePath(), VI))
9388bcb0991SDimitry Andric continue;
9398bcb0991SDimitry Andric
9408bcb0991SDimitry Andric // It's been exported by a cross module import.
9418bcb0991SDimitry Andric for (auto &SlotSummary : T.second) {
9428bcb0991SDimitry Andric auto *TIdSum = Summary.getTypeIdSummary(SlotSummary.TypeID);
9438bcb0991SDimitry Andric assert(TIdSum);
9448bcb0991SDimitry Andric auto WPDRes = TIdSum->WPDRes.find(SlotSummary.ByteOffset);
9458bcb0991SDimitry Andric assert(WPDRes != TIdSum->WPDRes.end());
9468bcb0991SDimitry Andric WPDRes->second.SingleImplName = ModuleSummaryIndex::getGlobalNameForLocal(
9478bcb0991SDimitry Andric WPDRes->second.SingleImplName,
9488bcb0991SDimitry Andric Summary.getModuleHash(S->modulePath()));
9498bcb0991SDimitry Andric }
9508bcb0991SDimitry Andric }
9518bcb0991SDimitry Andric }
9528bcb0991SDimitry Andric
checkCombinedSummaryForTesting(ModuleSummaryIndex * Summary)9535ffd83dbSDimitry Andric static Error checkCombinedSummaryForTesting(ModuleSummaryIndex *Summary) {
9545ffd83dbSDimitry Andric // Check that summary index contains regular LTO module when performing
9555ffd83dbSDimitry Andric // export to prevent occasional use of index from pure ThinLTO compilation
9565ffd83dbSDimitry Andric // (-fno-split-lto-module). This kind of summary index is passed to
9575ffd83dbSDimitry Andric // DevirtIndex::run, not to DevirtModule::run used by opt/runForTesting.
9585ffd83dbSDimitry Andric const auto &ModPaths = Summary->modulePaths();
9595ffd83dbSDimitry Andric if (ClSummaryAction != PassSummaryAction::Import &&
96006c3fb27SDimitry Andric !ModPaths.contains(ModuleSummaryIndex::getRegularLTOModuleName()))
9615ffd83dbSDimitry Andric return createStringError(
9625ffd83dbSDimitry Andric errc::invalid_argument,
9635ffd83dbSDimitry Andric "combined summary should contain Regular LTO module");
9645ffd83dbSDimitry Andric return ErrorSuccess();
9655ffd83dbSDimitry Andric }
9665ffd83dbSDimitry Andric
runForTesting(Module & M,function_ref<AAResults & (Function &)> AARGetter,function_ref<OptimizationRemarkEmitter & (Function *)> OREGetter,function_ref<DominatorTree & (Function &)> LookupDomTree)9670b57cec5SDimitry Andric bool DevirtModule::runForTesting(
9680b57cec5SDimitry Andric Module &M, function_ref<AAResults &(Function &)> AARGetter,
9690b57cec5SDimitry Andric function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter,
9700b57cec5SDimitry Andric function_ref<DominatorTree &(Function &)> LookupDomTree) {
9715ffd83dbSDimitry Andric std::unique_ptr<ModuleSummaryIndex> Summary =
9725ffd83dbSDimitry Andric std::make_unique<ModuleSummaryIndex>(/*HaveGVs=*/false);
9730b57cec5SDimitry Andric
9740b57cec5SDimitry Andric // Handle the command-line summary arguments. This code is for testing
9750b57cec5SDimitry Andric // purposes only, so we handle errors directly.
9760b57cec5SDimitry Andric if (!ClReadSummary.empty()) {
9770b57cec5SDimitry Andric ExitOnError ExitOnErr("-wholeprogramdevirt-read-summary: " + ClReadSummary +
9780b57cec5SDimitry Andric ": ");
9790b57cec5SDimitry Andric auto ReadSummaryFile =
9800b57cec5SDimitry Andric ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(ClReadSummary)));
9815ffd83dbSDimitry Andric if (Expected<std::unique_ptr<ModuleSummaryIndex>> SummaryOrErr =
9825ffd83dbSDimitry Andric getModuleSummaryIndex(*ReadSummaryFile)) {
9835ffd83dbSDimitry Andric Summary = std::move(*SummaryOrErr);
9845ffd83dbSDimitry Andric ExitOnErr(checkCombinedSummaryForTesting(Summary.get()));
9855ffd83dbSDimitry Andric } else {
9865ffd83dbSDimitry Andric // Try YAML if we've failed with bitcode.
9875ffd83dbSDimitry Andric consumeError(SummaryOrErr.takeError());
9880b57cec5SDimitry Andric yaml::Input In(ReadSummaryFile->getBuffer());
9895ffd83dbSDimitry Andric In >> *Summary;
9900b57cec5SDimitry Andric ExitOnErr(errorCodeToError(In.error()));
9910b57cec5SDimitry Andric }
9925ffd83dbSDimitry Andric }
9930b57cec5SDimitry Andric
9940b57cec5SDimitry Andric bool Changed =
9955ffd83dbSDimitry Andric DevirtModule(M, AARGetter, OREGetter, LookupDomTree,
9965ffd83dbSDimitry Andric ClSummaryAction == PassSummaryAction::Export ? Summary.get()
9975ffd83dbSDimitry Andric : nullptr,
9985ffd83dbSDimitry Andric ClSummaryAction == PassSummaryAction::Import ? Summary.get()
9995ffd83dbSDimitry Andric : nullptr)
10000b57cec5SDimitry Andric .run();
10010b57cec5SDimitry Andric
10020b57cec5SDimitry Andric if (!ClWriteSummary.empty()) {
10030b57cec5SDimitry Andric ExitOnError ExitOnErr(
10040b57cec5SDimitry Andric "-wholeprogramdevirt-write-summary: " + ClWriteSummary + ": ");
10050b57cec5SDimitry Andric std::error_code EC;
10065f757f3fSDimitry Andric if (StringRef(ClWriteSummary).ends_with(".bc")) {
10075ffd83dbSDimitry Andric raw_fd_ostream OS(ClWriteSummary, EC, sys::fs::OF_None);
10085ffd83dbSDimitry Andric ExitOnErr(errorCodeToError(EC));
10091fd87a68SDimitry Andric writeIndexToFile(*Summary, OS);
10105ffd83dbSDimitry Andric } else {
1011fe6060f1SDimitry Andric raw_fd_ostream OS(ClWriteSummary, EC, sys::fs::OF_TextWithCRLF);
10120b57cec5SDimitry Andric ExitOnErr(errorCodeToError(EC));
10130b57cec5SDimitry Andric yaml::Output Out(OS);
10145ffd83dbSDimitry Andric Out << *Summary;
10155ffd83dbSDimitry Andric }
10160b57cec5SDimitry Andric }
10170b57cec5SDimitry Andric
10180b57cec5SDimitry Andric return Changed;
10190b57cec5SDimitry Andric }
10200b57cec5SDimitry Andric
buildTypeIdentifierMap(std::vector<VTableBits> & Bits,DenseMap<Metadata *,std::set<TypeMemberInfo>> & TypeIdMap)10210b57cec5SDimitry Andric void DevirtModule::buildTypeIdentifierMap(
10220b57cec5SDimitry Andric std::vector<VTableBits> &Bits,
10230b57cec5SDimitry Andric DenseMap<Metadata *, std::set<TypeMemberInfo>> &TypeIdMap) {
10240b57cec5SDimitry Andric DenseMap<GlobalVariable *, VTableBits *> GVToBits;
102506c3fb27SDimitry Andric Bits.reserve(M.global_size());
10260b57cec5SDimitry Andric SmallVector<MDNode *, 2> Types;
10270b57cec5SDimitry Andric for (GlobalVariable &GV : M.globals()) {
10280b57cec5SDimitry Andric Types.clear();
10290b57cec5SDimitry Andric GV.getMetadata(LLVMContext::MD_type, Types);
10300b57cec5SDimitry Andric if (GV.isDeclaration() || Types.empty())
10310b57cec5SDimitry Andric continue;
10320b57cec5SDimitry Andric
10330b57cec5SDimitry Andric VTableBits *&BitsPtr = GVToBits[&GV];
10340b57cec5SDimitry Andric if (!BitsPtr) {
10350b57cec5SDimitry Andric Bits.emplace_back();
10360b57cec5SDimitry Andric Bits.back().GV = &GV;
10370b57cec5SDimitry Andric Bits.back().ObjectSize =
10380b57cec5SDimitry Andric M.getDataLayout().getTypeAllocSize(GV.getInitializer()->getType());
10390b57cec5SDimitry Andric BitsPtr = &Bits.back();
10400b57cec5SDimitry Andric }
10410b57cec5SDimitry Andric
10420b57cec5SDimitry Andric for (MDNode *Type : Types) {
10430b57cec5SDimitry Andric auto TypeID = Type->getOperand(1).get();
10440b57cec5SDimitry Andric
10450b57cec5SDimitry Andric uint64_t Offset =
10460b57cec5SDimitry Andric cast<ConstantInt>(
10470b57cec5SDimitry Andric cast<ConstantAsMetadata>(Type->getOperand(0))->getValue())
10480b57cec5SDimitry Andric ->getZExtValue();
10490b57cec5SDimitry Andric
10500b57cec5SDimitry Andric TypeIdMap[TypeID].insert({BitsPtr, Offset});
10510b57cec5SDimitry Andric }
10520b57cec5SDimitry Andric }
10530b57cec5SDimitry Andric }
10540b57cec5SDimitry Andric
tryFindVirtualCallTargets(std::vector<VirtualCallTarget> & TargetsForSlot,const std::set<TypeMemberInfo> & TypeMemberInfos,uint64_t ByteOffset,ModuleSummaryIndex * ExportSummary)10550b57cec5SDimitry Andric bool DevirtModule::tryFindVirtualCallTargets(
10560b57cec5SDimitry Andric std::vector<VirtualCallTarget> &TargetsForSlot,
10570eae32dcSDimitry Andric const std::set<TypeMemberInfo> &TypeMemberInfos, uint64_t ByteOffset,
10580eae32dcSDimitry Andric ModuleSummaryIndex *ExportSummary) {
10590b57cec5SDimitry Andric for (const TypeMemberInfo &TM : TypeMemberInfos) {
10600b57cec5SDimitry Andric if (!TM.Bits->GV->isConstant())
10610b57cec5SDimitry Andric return false;
10620b57cec5SDimitry Andric
10635ffd83dbSDimitry Andric // We cannot perform whole program devirtualization analysis on a vtable
10645ffd83dbSDimitry Andric // with public LTO visibility.
10655ffd83dbSDimitry Andric if (TM.Bits->GV->getVCallVisibility() ==
10665ffd83dbSDimitry Andric GlobalObject::VCallVisibilityPublic)
10675ffd83dbSDimitry Andric return false;
10685ffd83dbSDimitry Andric
1069*0fca6ea1SDimitry Andric Function *Fn = nullptr;
1070*0fca6ea1SDimitry Andric Constant *C = nullptr;
1071*0fca6ea1SDimitry Andric std::tie(Fn, C) =
1072*0fca6ea1SDimitry Andric getFunctionAtVTableOffset(TM.Bits->GV, TM.Offset + ByteOffset, M);
107306c3fb27SDimitry Andric
10740b57cec5SDimitry Andric if (!Fn)
10750b57cec5SDimitry Andric return false;
10760b57cec5SDimitry Andric
10775ffd83dbSDimitry Andric if (FunctionsToSkip.match(Fn->getName()))
10785ffd83dbSDimitry Andric return false;
10795ffd83dbSDimitry Andric
10800b57cec5SDimitry Andric // We can disregard __cxa_pure_virtual as a possible call target, as
10810b57cec5SDimitry Andric // calls to pure virtuals are UB.
10820b57cec5SDimitry Andric if (Fn->getName() == "__cxa_pure_virtual")
10830b57cec5SDimitry Andric continue;
10840b57cec5SDimitry Andric
10850eae32dcSDimitry Andric // We can disregard unreachable functions as possible call targets, as
10860eae32dcSDimitry Andric // unreachable functions shouldn't be called.
10870eae32dcSDimitry Andric if (mustBeUnreachableFunction(Fn, ExportSummary))
10880eae32dcSDimitry Andric continue;
10890eae32dcSDimitry Andric
109006c3fb27SDimitry Andric // Save the symbol used in the vtable to use as the devirtualization
109106c3fb27SDimitry Andric // target.
109206c3fb27SDimitry Andric auto GV = dyn_cast<GlobalValue>(C);
109306c3fb27SDimitry Andric assert(GV);
109406c3fb27SDimitry Andric TargetsForSlot.push_back({GV, &TM});
10950b57cec5SDimitry Andric }
10960b57cec5SDimitry Andric
10970b57cec5SDimitry Andric // Give up if we couldn't find any targets.
10980b57cec5SDimitry Andric return !TargetsForSlot.empty();
10990b57cec5SDimitry Andric }
11000b57cec5SDimitry Andric
tryFindVirtualCallTargets(std::vector<ValueInfo> & TargetsForSlot,const TypeIdCompatibleVtableInfo TIdInfo,uint64_t ByteOffset)11018bcb0991SDimitry Andric bool DevirtIndex::tryFindVirtualCallTargets(
11025f757f3fSDimitry Andric std::vector<ValueInfo> &TargetsForSlot,
11035f757f3fSDimitry Andric const TypeIdCompatibleVtableInfo TIdInfo, uint64_t ByteOffset) {
1104480093f4SDimitry Andric for (const TypeIdOffsetVtableInfo &P : TIdInfo) {
1105fe6060f1SDimitry Andric // Find a representative copy of the vtable initializer.
1106480093f4SDimitry Andric // We can have multiple available_externally, linkonce_odr and weak_odr
1107fe6060f1SDimitry Andric // vtable initializers. We can also have multiple external vtable
1108fe6060f1SDimitry Andric // initializers in the case of comdats, which we cannot check here.
1109480093f4SDimitry Andric // The linker should give an error in this case.
1110480093f4SDimitry Andric //
1111480093f4SDimitry Andric // Also, handle the case of same-named local Vtables with the same path
1112480093f4SDimitry Andric // and therefore the same GUID. This can happen if there isn't enough
1113480093f4SDimitry Andric // distinguishing path when compiling the source file. In that case we
1114480093f4SDimitry Andric // conservatively return false early.
1115480093f4SDimitry Andric const GlobalVarSummary *VS = nullptr;
1116480093f4SDimitry Andric bool LocalFound = false;
1117bdd1243dSDimitry Andric for (const auto &S : P.VTableVI.getSummaryList()) {
1118480093f4SDimitry Andric if (GlobalValue::isLocalLinkage(S->linkage())) {
1119480093f4SDimitry Andric if (LocalFound)
1120480093f4SDimitry Andric return false;
1121480093f4SDimitry Andric LocalFound = true;
1122480093f4SDimitry Andric }
1123fe6060f1SDimitry Andric auto *CurVS = cast<GlobalVarSummary>(S->getBaseObject());
1124fe6060f1SDimitry Andric if (!CurVS->vTableFuncs().empty() ||
1125fe6060f1SDimitry Andric // Previously clang did not attach the necessary type metadata to
1126fe6060f1SDimitry Andric // available_externally vtables, in which case there would not
1127fe6060f1SDimitry Andric // be any vtable functions listed in the summary and we need
1128fe6060f1SDimitry Andric // to treat this case conservatively (in case the bitcode is old).
1129fe6060f1SDimitry Andric // However, we will also not have any vtable functions in the
1130fe6060f1SDimitry Andric // case of a pure virtual base class. In that case we do want
1131fe6060f1SDimitry Andric // to set VS to avoid treating it conservatively.
1132fe6060f1SDimitry Andric !GlobalValue::isAvailableExternallyLinkage(S->linkage())) {
1133fe6060f1SDimitry Andric VS = CurVS;
11345ffd83dbSDimitry Andric // We cannot perform whole program devirtualization analysis on a vtable
11355ffd83dbSDimitry Andric // with public LTO visibility.
11365ffd83dbSDimitry Andric if (VS->getVCallVisibility() == GlobalObject::VCallVisibilityPublic)
11375ffd83dbSDimitry Andric return false;
11385ffd83dbSDimitry Andric }
1139480093f4SDimitry Andric }
1140fe6060f1SDimitry Andric // There will be no VS if all copies are available_externally having no
1141fe6060f1SDimitry Andric // type metadata. In that case we can't safely perform WPD.
1142fe6060f1SDimitry Andric if (!VS)
1143fe6060f1SDimitry Andric return false;
1144480093f4SDimitry Andric if (!VS->isLive())
11458bcb0991SDimitry Andric continue;
11468bcb0991SDimitry Andric for (auto VTP : VS->vTableFuncs()) {
11478bcb0991SDimitry Andric if (VTP.VTableOffset != P.AddressPointOffset + ByteOffset)
11488bcb0991SDimitry Andric continue;
11498bcb0991SDimitry Andric
11500eae32dcSDimitry Andric if (mustBeUnreachableFunction(VTP.FuncVI))
11510eae32dcSDimitry Andric continue;
11520eae32dcSDimitry Andric
11538bcb0991SDimitry Andric TargetsForSlot.push_back(VTP.FuncVI);
11548bcb0991SDimitry Andric }
11558bcb0991SDimitry Andric }
11568bcb0991SDimitry Andric
11578bcb0991SDimitry Andric // Give up if we couldn't find any targets.
11588bcb0991SDimitry Andric return !TargetsForSlot.empty();
11598bcb0991SDimitry Andric }
11608bcb0991SDimitry Andric
applySingleImplDevirt(VTableSlotInfo & SlotInfo,Constant * TheFn,bool & IsExported)11610b57cec5SDimitry Andric void DevirtModule::applySingleImplDevirt(VTableSlotInfo &SlotInfo,
11620b57cec5SDimitry Andric Constant *TheFn, bool &IsExported) {
1163e8d8bef9SDimitry Andric // Don't devirtualize function if we're told to skip it
1164e8d8bef9SDimitry Andric // in -wholeprogramdevirt-skip.
1165e8d8bef9SDimitry Andric if (FunctionsToSkip.match(TheFn->stripPointerCasts()->getName()))
1166e8d8bef9SDimitry Andric return;
11670b57cec5SDimitry Andric auto Apply = [&](CallSiteInfo &CSInfo) {
11680b57cec5SDimitry Andric for (auto &&VCallSite : CSInfo.CallSites) {
1169fe6060f1SDimitry Andric if (!OptimizedCalls.insert(&VCallSite.CB).second)
1170fe6060f1SDimitry Andric continue;
1171fe6060f1SDimitry Andric
11720b57cec5SDimitry Andric if (RemarksEnabled)
11730b57cec5SDimitry Andric VCallSite.emitRemark("single-impl",
11740b57cec5SDimitry Andric TheFn->stripPointerCasts()->getName(), OREGetter);
117581ad6265SDimitry Andric NumSingleImpl++;
1176fe6060f1SDimitry Andric auto &CB = VCallSite.CB;
1177fe6060f1SDimitry Andric assert(!CB.getCalledFunction() && "devirtualizing direct call?");
1178fe6060f1SDimitry Andric IRBuilder<> Builder(&CB);
1179fe6060f1SDimitry Andric Value *Callee =
1180fe6060f1SDimitry Andric Builder.CreateBitCast(TheFn, CB.getCalledOperand()->getType());
1181fe6060f1SDimitry Andric
118281ad6265SDimitry Andric // If trap checking is enabled, add support to compare the virtual
118381ad6265SDimitry Andric // function pointer to the devirtualized target. In case of a mismatch,
118481ad6265SDimitry Andric // perform a debug trap.
118581ad6265SDimitry Andric if (DevirtCheckMode == WPDCheckMode::Trap) {
1186fe6060f1SDimitry Andric auto *Cond = Builder.CreateICmpNE(CB.getCalledOperand(), Callee);
1187fe6060f1SDimitry Andric Instruction *ThenTerm =
1188fe6060f1SDimitry Andric SplitBlockAndInsertIfThen(Cond, &CB, /*Unreachable=*/false);
1189fe6060f1SDimitry Andric Builder.SetInsertPoint(ThenTerm);
1190fe6060f1SDimitry Andric Function *TrapFn = Intrinsic::getDeclaration(&M, Intrinsic::debugtrap);
1191fe6060f1SDimitry Andric auto *CallTrap = Builder.CreateCall(TrapFn);
1192fe6060f1SDimitry Andric CallTrap->setDebugLoc(CB.getDebugLoc());
1193fe6060f1SDimitry Andric }
1194fe6060f1SDimitry Andric
119581ad6265SDimitry Andric // If fallback checking is enabled, add support to compare the virtual
119681ad6265SDimitry Andric // function pointer to the devirtualized target. In case of a mismatch,
119781ad6265SDimitry Andric // fall back to indirect call.
119881ad6265SDimitry Andric if (DevirtCheckMode == WPDCheckMode::Fallback) {
1199*0fca6ea1SDimitry Andric MDNode *Weights = MDBuilder(M.getContext()).createLikelyBranchWeights();
120081ad6265SDimitry Andric // Version the indirect call site. If the called value is equal to the
120181ad6265SDimitry Andric // given callee, 'NewInst' will be executed, otherwise the original call
120281ad6265SDimitry Andric // site will be executed.
120381ad6265SDimitry Andric CallBase &NewInst = versionCallSite(CB, Callee, Weights);
120481ad6265SDimitry Andric NewInst.setCalledOperand(Callee);
120581ad6265SDimitry Andric // Since the new call site is direct, we must clear metadata that
120681ad6265SDimitry Andric // is only appropriate for indirect calls. This includes !prof and
120781ad6265SDimitry Andric // !callees metadata.
120881ad6265SDimitry Andric NewInst.setMetadata(LLVMContext::MD_prof, nullptr);
120981ad6265SDimitry Andric NewInst.setMetadata(LLVMContext::MD_callees, nullptr);
121081ad6265SDimitry Andric // Additionally, we should remove them from the fallback indirect call,
121181ad6265SDimitry Andric // so that we don't attempt to perform indirect call promotion later.
121281ad6265SDimitry Andric CB.setMetadata(LLVMContext::MD_prof, nullptr);
121381ad6265SDimitry Andric CB.setMetadata(LLVMContext::MD_callees, nullptr);
121481ad6265SDimitry Andric }
121581ad6265SDimitry Andric
121681ad6265SDimitry Andric // In either trapping or non-checking mode, devirtualize original call.
121781ad6265SDimitry Andric else {
121881ad6265SDimitry Andric // Devirtualize unconditionally.
1219fe6060f1SDimitry Andric CB.setCalledOperand(Callee);
122081ad6265SDimitry Andric // Since the call site is now direct, we must clear metadata that
122181ad6265SDimitry Andric // is only appropriate for indirect calls. This includes !prof and
122281ad6265SDimitry Andric // !callees metadata.
122381ad6265SDimitry Andric CB.setMetadata(LLVMContext::MD_prof, nullptr);
122481ad6265SDimitry Andric CB.setMetadata(LLVMContext::MD_callees, nullptr);
122506c3fb27SDimitry Andric if (CB.getCalledOperand() &&
122606c3fb27SDimitry Andric CB.getOperandBundle(LLVMContext::OB_ptrauth)) {
1227*0fca6ea1SDimitry Andric auto *NewCS = CallBase::removeOperandBundle(
1228*0fca6ea1SDimitry Andric &CB, LLVMContext::OB_ptrauth, CB.getIterator());
122906c3fb27SDimitry Andric CB.replaceAllUsesWith(NewCS);
123006c3fb27SDimitry Andric // Schedule for deletion at the end of pass run.
123106c3fb27SDimitry Andric CallsWithPtrAuthBundleRemoved.push_back(&CB);
123206c3fb27SDimitry Andric }
123381ad6265SDimitry Andric }
1234fe6060f1SDimitry Andric
12350b57cec5SDimitry Andric // This use is no longer unsafe.
12360b57cec5SDimitry Andric if (VCallSite.NumUnsafeUses)
12370b57cec5SDimitry Andric --*VCallSite.NumUnsafeUses;
12380b57cec5SDimitry Andric }
12390b57cec5SDimitry Andric if (CSInfo.isExported())
12400b57cec5SDimitry Andric IsExported = true;
12410b57cec5SDimitry Andric CSInfo.markDevirt();
12420b57cec5SDimitry Andric };
12430b57cec5SDimitry Andric Apply(SlotInfo.CSInfo);
12440b57cec5SDimitry Andric for (auto &P : SlotInfo.ConstCSInfo)
12450b57cec5SDimitry Andric Apply(P.second);
12460b57cec5SDimitry Andric }
12470b57cec5SDimitry Andric
AddCalls(VTableSlotInfo & SlotInfo,const ValueInfo & Callee)12488bcb0991SDimitry Andric static bool AddCalls(VTableSlotInfo &SlotInfo, const ValueInfo &Callee) {
12498bcb0991SDimitry Andric // We can't add calls if we haven't seen a definition
12508bcb0991SDimitry Andric if (Callee.getSummaryList().empty())
12518bcb0991SDimitry Andric return false;
12528bcb0991SDimitry Andric
12538bcb0991SDimitry Andric // Insert calls into the summary index so that the devirtualized targets
12548bcb0991SDimitry Andric // are eligible for import.
12558bcb0991SDimitry Andric // FIXME: Annotate type tests with hotness. For now, mark these as hot
12568bcb0991SDimitry Andric // to better ensure we have the opportunity to inline them.
12578bcb0991SDimitry Andric bool IsExported = false;
12588bcb0991SDimitry Andric auto &S = Callee.getSummaryList()[0];
12595f757f3fSDimitry Andric CalleeInfo CI(CalleeInfo::HotnessType::Hot, /* HasTailCall = */ false,
12605f757f3fSDimitry Andric /* RelBF = */ 0);
12618bcb0991SDimitry Andric auto AddCalls = [&](CallSiteInfo &CSInfo) {
12628bcb0991SDimitry Andric for (auto *FS : CSInfo.SummaryTypeCheckedLoadUsers) {
12638bcb0991SDimitry Andric FS->addCall({Callee, CI});
12648bcb0991SDimitry Andric IsExported |= S->modulePath() != FS->modulePath();
12658bcb0991SDimitry Andric }
12668bcb0991SDimitry Andric for (auto *FS : CSInfo.SummaryTypeTestAssumeUsers) {
12678bcb0991SDimitry Andric FS->addCall({Callee, CI});
12688bcb0991SDimitry Andric IsExported |= S->modulePath() != FS->modulePath();
12698bcb0991SDimitry Andric }
12708bcb0991SDimitry Andric };
12718bcb0991SDimitry Andric AddCalls(SlotInfo.CSInfo);
12728bcb0991SDimitry Andric for (auto &P : SlotInfo.ConstCSInfo)
12738bcb0991SDimitry Andric AddCalls(P.second);
12748bcb0991SDimitry Andric return IsExported;
12758bcb0991SDimitry Andric }
12768bcb0991SDimitry Andric
trySingleImplDevirt(ModuleSummaryIndex * ExportSummary,MutableArrayRef<VirtualCallTarget> TargetsForSlot,VTableSlotInfo & SlotInfo,WholeProgramDevirtResolution * Res)12770b57cec5SDimitry Andric bool DevirtModule::trySingleImplDevirt(
12788bcb0991SDimitry Andric ModuleSummaryIndex *ExportSummary,
12798bcb0991SDimitry Andric MutableArrayRef<VirtualCallTarget> TargetsForSlot, VTableSlotInfo &SlotInfo,
12808bcb0991SDimitry Andric WholeProgramDevirtResolution *Res) {
12810b57cec5SDimitry Andric // See if the program contains a single implementation of this virtual
12820b57cec5SDimitry Andric // function.
128306c3fb27SDimitry Andric auto *TheFn = TargetsForSlot[0].Fn;
12840b57cec5SDimitry Andric for (auto &&Target : TargetsForSlot)
12850b57cec5SDimitry Andric if (TheFn != Target.Fn)
12860b57cec5SDimitry Andric return false;
12870b57cec5SDimitry Andric
12880b57cec5SDimitry Andric // If so, update each call site to call that implementation directly.
128981ad6265SDimitry Andric if (RemarksEnabled || AreStatisticsEnabled())
12900b57cec5SDimitry Andric TargetsForSlot[0].WasDevirt = true;
12910b57cec5SDimitry Andric
12920b57cec5SDimitry Andric bool IsExported = false;
12930b57cec5SDimitry Andric applySingleImplDevirt(SlotInfo, TheFn, IsExported);
12940b57cec5SDimitry Andric if (!IsExported)
12950b57cec5SDimitry Andric return false;
12960b57cec5SDimitry Andric
12970b57cec5SDimitry Andric // If the only implementation has local linkage, we must promote to external
12980b57cec5SDimitry Andric // to make it visible to thin LTO objects. We can only get here during the
12990b57cec5SDimitry Andric // ThinLTO export phase.
13000b57cec5SDimitry Andric if (TheFn->hasLocalLinkage()) {
1301fe6060f1SDimitry Andric std::string NewName = (TheFn->getName() + ".llvm.merged").str();
13020b57cec5SDimitry Andric
13030b57cec5SDimitry Andric // Since we are renaming the function, any comdats with the same name must
13040b57cec5SDimitry Andric // also be renamed. This is required when targeting COFF, as the comdat name
13050b57cec5SDimitry Andric // must match one of the names of the symbols in the comdat.
13060b57cec5SDimitry Andric if (Comdat *C = TheFn->getComdat()) {
13070b57cec5SDimitry Andric if (C->getName() == TheFn->getName()) {
13080b57cec5SDimitry Andric Comdat *NewC = M.getOrInsertComdat(NewName);
13090b57cec5SDimitry Andric NewC->setSelectionKind(C->getSelectionKind());
13100b57cec5SDimitry Andric for (GlobalObject &GO : M.global_objects())
13110b57cec5SDimitry Andric if (GO.getComdat() == C)
13120b57cec5SDimitry Andric GO.setComdat(NewC);
13130b57cec5SDimitry Andric }
13140b57cec5SDimitry Andric }
13150b57cec5SDimitry Andric
13160b57cec5SDimitry Andric TheFn->setLinkage(GlobalValue::ExternalLinkage);
13170b57cec5SDimitry Andric TheFn->setVisibility(GlobalValue::HiddenVisibility);
13180b57cec5SDimitry Andric TheFn->setName(NewName);
13190b57cec5SDimitry Andric }
13208bcb0991SDimitry Andric if (ValueInfo TheFnVI = ExportSummary->getValueInfo(TheFn->getGUID()))
13218bcb0991SDimitry Andric // Any needed promotion of 'TheFn' has already been done during
13228bcb0991SDimitry Andric // LTO unit split, so we can ignore return value of AddCalls.
13238bcb0991SDimitry Andric AddCalls(SlotInfo, TheFnVI);
13240b57cec5SDimitry Andric
13250b57cec5SDimitry Andric Res->TheKind = WholeProgramDevirtResolution::SingleImpl;
13265ffd83dbSDimitry Andric Res->SingleImplName = std::string(TheFn->getName());
13270b57cec5SDimitry Andric
13280b57cec5SDimitry Andric return true;
13290b57cec5SDimitry Andric }
13300b57cec5SDimitry Andric
trySingleImplDevirt(MutableArrayRef<ValueInfo> TargetsForSlot,VTableSlotSummary & SlotSummary,VTableSlotInfo & SlotInfo,WholeProgramDevirtResolution * Res,std::set<ValueInfo> & DevirtTargets)13318bcb0991SDimitry Andric bool DevirtIndex::trySingleImplDevirt(MutableArrayRef<ValueInfo> TargetsForSlot,
13328bcb0991SDimitry Andric VTableSlotSummary &SlotSummary,
13338bcb0991SDimitry Andric VTableSlotInfo &SlotInfo,
13348bcb0991SDimitry Andric WholeProgramDevirtResolution *Res,
13358bcb0991SDimitry Andric std::set<ValueInfo> &DevirtTargets) {
13368bcb0991SDimitry Andric // See if the program contains a single implementation of this virtual
13378bcb0991SDimitry Andric // function.
13388bcb0991SDimitry Andric auto TheFn = TargetsForSlot[0];
13398bcb0991SDimitry Andric for (auto &&Target : TargetsForSlot)
13408bcb0991SDimitry Andric if (TheFn != Target)
13418bcb0991SDimitry Andric return false;
13428bcb0991SDimitry Andric
13438bcb0991SDimitry Andric // Don't devirtualize if we don't have target definition.
13448bcb0991SDimitry Andric auto Size = TheFn.getSummaryList().size();
13458bcb0991SDimitry Andric if (!Size)
13468bcb0991SDimitry Andric return false;
13478bcb0991SDimitry Andric
13485ffd83dbSDimitry Andric // Don't devirtualize function if we're told to skip it
13495ffd83dbSDimitry Andric // in -wholeprogramdevirt-skip.
13505ffd83dbSDimitry Andric if (FunctionsToSkip.match(TheFn.name()))
13515ffd83dbSDimitry Andric return false;
13525ffd83dbSDimitry Andric
13538bcb0991SDimitry Andric // If the summary list contains multiple summaries where at least one is
13548bcb0991SDimitry Andric // a local, give up, as we won't know which (possibly promoted) name to use.
1355bdd1243dSDimitry Andric for (const auto &S : TheFn.getSummaryList())
13568bcb0991SDimitry Andric if (GlobalValue::isLocalLinkage(S->linkage()) && Size > 1)
13578bcb0991SDimitry Andric return false;
13588bcb0991SDimitry Andric
13598bcb0991SDimitry Andric // Collect functions devirtualized at least for one call site for stats.
136081ad6265SDimitry Andric if (PrintSummaryDevirt || AreStatisticsEnabled())
13618bcb0991SDimitry Andric DevirtTargets.insert(TheFn);
13628bcb0991SDimitry Andric
13638bcb0991SDimitry Andric auto &S = TheFn.getSummaryList()[0];
13648bcb0991SDimitry Andric bool IsExported = AddCalls(SlotInfo, TheFn);
13658bcb0991SDimitry Andric if (IsExported)
13668bcb0991SDimitry Andric ExportedGUIDs.insert(TheFn.getGUID());
13678bcb0991SDimitry Andric
13688bcb0991SDimitry Andric // Record in summary for use in devirtualization during the ThinLTO import
13698bcb0991SDimitry Andric // step.
13708bcb0991SDimitry Andric Res->TheKind = WholeProgramDevirtResolution::SingleImpl;
13718bcb0991SDimitry Andric if (GlobalValue::isLocalLinkage(S->linkage())) {
13728bcb0991SDimitry Andric if (IsExported)
13738bcb0991SDimitry Andric // If target is a local function and we are exporting it by
13748bcb0991SDimitry Andric // devirtualizing a call in another module, we need to record the
13758bcb0991SDimitry Andric // promoted name.
13768bcb0991SDimitry Andric Res->SingleImplName = ModuleSummaryIndex::getGlobalNameForLocal(
13778bcb0991SDimitry Andric TheFn.name(), ExportSummary.getModuleHash(S->modulePath()));
13788bcb0991SDimitry Andric else {
13798bcb0991SDimitry Andric LocalWPDTargetsMap[TheFn].push_back(SlotSummary);
13805ffd83dbSDimitry Andric Res->SingleImplName = std::string(TheFn.name());
13818bcb0991SDimitry Andric }
13828bcb0991SDimitry Andric } else
13835ffd83dbSDimitry Andric Res->SingleImplName = std::string(TheFn.name());
13848bcb0991SDimitry Andric
13858bcb0991SDimitry Andric // Name will be empty if this thin link driven off of serialized combined
13868bcb0991SDimitry Andric // index (e.g. llvm-lto). However, WPD is not supported/invoked for the
13878bcb0991SDimitry Andric // legacy LTO API anyway.
13888bcb0991SDimitry Andric assert(!Res->SingleImplName.empty());
13898bcb0991SDimitry Andric
13908bcb0991SDimitry Andric return true;
13918bcb0991SDimitry Andric }
13928bcb0991SDimitry Andric
tryICallBranchFunnel(MutableArrayRef<VirtualCallTarget> TargetsForSlot,VTableSlotInfo & SlotInfo,WholeProgramDevirtResolution * Res,VTableSlot Slot)13930b57cec5SDimitry Andric void DevirtModule::tryICallBranchFunnel(
13940b57cec5SDimitry Andric MutableArrayRef<VirtualCallTarget> TargetsForSlot, VTableSlotInfo &SlotInfo,
13950b57cec5SDimitry Andric WholeProgramDevirtResolution *Res, VTableSlot Slot) {
13960b57cec5SDimitry Andric Triple T(M.getTargetTriple());
13970b57cec5SDimitry Andric if (T.getArch() != Triple::x86_64)
13980b57cec5SDimitry Andric return;
13990b57cec5SDimitry Andric
14000b57cec5SDimitry Andric if (TargetsForSlot.size() > ClThreshold)
14010b57cec5SDimitry Andric return;
14020b57cec5SDimitry Andric
14030b57cec5SDimitry Andric bool HasNonDevirt = !SlotInfo.CSInfo.AllCallSitesDevirted;
14040b57cec5SDimitry Andric if (!HasNonDevirt)
14050b57cec5SDimitry Andric for (auto &P : SlotInfo.ConstCSInfo)
14060b57cec5SDimitry Andric if (!P.second.AllCallSitesDevirted) {
14070b57cec5SDimitry Andric HasNonDevirt = true;
14080b57cec5SDimitry Andric break;
14090b57cec5SDimitry Andric }
14100b57cec5SDimitry Andric
14110b57cec5SDimitry Andric if (!HasNonDevirt)
14120b57cec5SDimitry Andric return;
14130b57cec5SDimitry Andric
14140b57cec5SDimitry Andric FunctionType *FT =
14150b57cec5SDimitry Andric FunctionType::get(Type::getVoidTy(M.getContext()), {Int8PtrTy}, true);
14160b57cec5SDimitry Andric Function *JT;
14170b57cec5SDimitry Andric if (isa<MDString>(Slot.TypeID)) {
14180b57cec5SDimitry Andric JT = Function::Create(FT, Function::ExternalLinkage,
14190b57cec5SDimitry Andric M.getDataLayout().getProgramAddressSpace(),
14200b57cec5SDimitry Andric getGlobalName(Slot, {}, "branch_funnel"), &M);
14210b57cec5SDimitry Andric JT->setVisibility(GlobalValue::HiddenVisibility);
14220b57cec5SDimitry Andric } else {
14230b57cec5SDimitry Andric JT = Function::Create(FT, Function::InternalLinkage,
14240b57cec5SDimitry Andric M.getDataLayout().getProgramAddressSpace(),
14250b57cec5SDimitry Andric "branch_funnel", &M);
14260b57cec5SDimitry Andric }
1427349cc55cSDimitry Andric JT->addParamAttr(0, Attribute::Nest);
14280b57cec5SDimitry Andric
14290b57cec5SDimitry Andric std::vector<Value *> JTArgs;
14300b57cec5SDimitry Andric JTArgs.push_back(JT->arg_begin());
14310b57cec5SDimitry Andric for (auto &T : TargetsForSlot) {
14320b57cec5SDimitry Andric JTArgs.push_back(getMemberAddr(T.TM));
14330b57cec5SDimitry Andric JTArgs.push_back(T.Fn);
14340b57cec5SDimitry Andric }
14350b57cec5SDimitry Andric
14360b57cec5SDimitry Andric BasicBlock *BB = BasicBlock::Create(M.getContext(), "", JT, nullptr);
14370b57cec5SDimitry Andric Function *Intr =
14380b57cec5SDimitry Andric Intrinsic::getDeclaration(&M, llvm::Intrinsic::icall_branch_funnel, {});
14390b57cec5SDimitry Andric
14400b57cec5SDimitry Andric auto *CI = CallInst::Create(Intr, JTArgs, "", BB);
14410b57cec5SDimitry Andric CI->setTailCallKind(CallInst::TCK_MustTail);
14420b57cec5SDimitry Andric ReturnInst::Create(M.getContext(), nullptr, BB);
14430b57cec5SDimitry Andric
14440b57cec5SDimitry Andric bool IsExported = false;
14450b57cec5SDimitry Andric applyICallBranchFunnel(SlotInfo, JT, IsExported);
14460b57cec5SDimitry Andric if (IsExported)
14470b57cec5SDimitry Andric Res->TheKind = WholeProgramDevirtResolution::BranchFunnel;
14480b57cec5SDimitry Andric }
14490b57cec5SDimitry Andric
applyICallBranchFunnel(VTableSlotInfo & SlotInfo,Constant * JT,bool & IsExported)14500b57cec5SDimitry Andric void DevirtModule::applyICallBranchFunnel(VTableSlotInfo &SlotInfo,
14510b57cec5SDimitry Andric Constant *JT, bool &IsExported) {
14520b57cec5SDimitry Andric auto Apply = [&](CallSiteInfo &CSInfo) {
14530b57cec5SDimitry Andric if (CSInfo.isExported())
14540b57cec5SDimitry Andric IsExported = true;
14550b57cec5SDimitry Andric if (CSInfo.AllCallSitesDevirted)
14560b57cec5SDimitry Andric return;
145706c3fb27SDimitry Andric
145806c3fb27SDimitry Andric std::map<CallBase *, CallBase *> CallBases;
14590b57cec5SDimitry Andric for (auto &&VCallSite : CSInfo.CallSites) {
14605ffd83dbSDimitry Andric CallBase &CB = VCallSite.CB;
14610b57cec5SDimitry Andric
146206c3fb27SDimitry Andric if (CallBases.find(&CB) != CallBases.end()) {
146306c3fb27SDimitry Andric // When finding devirtualizable calls, it's possible to find the same
146406c3fb27SDimitry Andric // vtable passed to multiple llvm.type.test or llvm.type.checked.load
146506c3fb27SDimitry Andric // calls, which can cause duplicate call sites to be recorded in
146606c3fb27SDimitry Andric // [Const]CallSites. If we've already found one of these
146706c3fb27SDimitry Andric // call instances, just ignore it. It will be replaced later.
146806c3fb27SDimitry Andric continue;
146906c3fb27SDimitry Andric }
147006c3fb27SDimitry Andric
14710b57cec5SDimitry Andric // Jump tables are only profitable if the retpoline mitigation is enabled.
14725ffd83dbSDimitry Andric Attribute FSAttr = CB.getCaller()->getFnAttribute("target-features");
1473e8d8bef9SDimitry Andric if (!FSAttr.isValid() ||
14740b57cec5SDimitry Andric !FSAttr.getValueAsString().contains("+retpoline"))
14750b57cec5SDimitry Andric continue;
14760b57cec5SDimitry Andric
147781ad6265SDimitry Andric NumBranchFunnel++;
14780b57cec5SDimitry Andric if (RemarksEnabled)
14790b57cec5SDimitry Andric VCallSite.emitRemark("branch-funnel",
14800b57cec5SDimitry Andric JT->stripPointerCasts()->getName(), OREGetter);
14810b57cec5SDimitry Andric
14820b57cec5SDimitry Andric // Pass the address of the vtable in the nest register, which is r10 on
14830b57cec5SDimitry Andric // x86_64.
14840b57cec5SDimitry Andric std::vector<Type *> NewArgs;
14850b57cec5SDimitry Andric NewArgs.push_back(Int8PtrTy);
1486e8d8bef9SDimitry Andric append_range(NewArgs, CB.getFunctionType()->params());
14870b57cec5SDimitry Andric FunctionType *NewFT =
14885ffd83dbSDimitry Andric FunctionType::get(CB.getFunctionType()->getReturnType(), NewArgs,
14895ffd83dbSDimitry Andric CB.getFunctionType()->isVarArg());
14900b57cec5SDimitry Andric PointerType *NewFTPtr = PointerType::getUnqual(NewFT);
14910b57cec5SDimitry Andric
14925ffd83dbSDimitry Andric IRBuilder<> IRB(&CB);
14930b57cec5SDimitry Andric std::vector<Value *> Args;
14945f757f3fSDimitry Andric Args.push_back(VCallSite.VTable);
1495e8d8bef9SDimitry Andric llvm::append_range(Args, CB.args());
14960b57cec5SDimitry Andric
14975ffd83dbSDimitry Andric CallBase *NewCS = nullptr;
14985ffd83dbSDimitry Andric if (isa<CallInst>(CB))
14990b57cec5SDimitry Andric NewCS = IRB.CreateCall(NewFT, IRB.CreateBitCast(JT, NewFTPtr), Args);
15000b57cec5SDimitry Andric else
15015ffd83dbSDimitry Andric NewCS = IRB.CreateInvoke(NewFT, IRB.CreateBitCast(JT, NewFTPtr),
15025ffd83dbSDimitry Andric cast<InvokeInst>(CB).getNormalDest(),
15035ffd83dbSDimitry Andric cast<InvokeInst>(CB).getUnwindDest(), Args);
15045ffd83dbSDimitry Andric NewCS->setCallingConv(CB.getCallingConv());
15050b57cec5SDimitry Andric
15065ffd83dbSDimitry Andric AttributeList Attrs = CB.getAttributes();
15070b57cec5SDimitry Andric std::vector<AttributeSet> NewArgAttrs;
15080b57cec5SDimitry Andric NewArgAttrs.push_back(AttributeSet::get(
15090b57cec5SDimitry Andric M.getContext(), ArrayRef<Attribute>{Attribute::get(
15100b57cec5SDimitry Andric M.getContext(), Attribute::Nest)}));
15110b57cec5SDimitry Andric for (unsigned I = 0; I + 2 < Attrs.getNumAttrSets(); ++I)
1512349cc55cSDimitry Andric NewArgAttrs.push_back(Attrs.getParamAttrs(I));
15135ffd83dbSDimitry Andric NewCS->setAttributes(
1514349cc55cSDimitry Andric AttributeList::get(M.getContext(), Attrs.getFnAttrs(),
1515349cc55cSDimitry Andric Attrs.getRetAttrs(), NewArgAttrs));
15160b57cec5SDimitry Andric
151706c3fb27SDimitry Andric CallBases[&CB] = NewCS;
15180b57cec5SDimitry Andric
15190b57cec5SDimitry Andric // This use is no longer unsafe.
15200b57cec5SDimitry Andric if (VCallSite.NumUnsafeUses)
15210b57cec5SDimitry Andric --*VCallSite.NumUnsafeUses;
15220b57cec5SDimitry Andric }
15230b57cec5SDimitry Andric // Don't mark as devirtualized because there may be callers compiled without
15240b57cec5SDimitry Andric // retpoline mitigation, which would mean that they are lowered to
15250b57cec5SDimitry Andric // llvm.type.test and therefore require an llvm.type.test resolution for the
15260b57cec5SDimitry Andric // type identifier.
152706c3fb27SDimitry Andric
15285f757f3fSDimitry Andric for (auto &[Old, New] : CallBases) {
15295f757f3fSDimitry Andric Old->replaceAllUsesWith(New);
15305f757f3fSDimitry Andric Old->eraseFromParent();
15315f757f3fSDimitry Andric }
15320b57cec5SDimitry Andric };
15330b57cec5SDimitry Andric Apply(SlotInfo.CSInfo);
15340b57cec5SDimitry Andric for (auto &P : SlotInfo.ConstCSInfo)
15350b57cec5SDimitry Andric Apply(P.second);
15360b57cec5SDimitry Andric }
15370b57cec5SDimitry Andric
tryEvaluateFunctionsWithArgs(MutableArrayRef<VirtualCallTarget> TargetsForSlot,ArrayRef<uint64_t> Args)15380b57cec5SDimitry Andric bool DevirtModule::tryEvaluateFunctionsWithArgs(
15390b57cec5SDimitry Andric MutableArrayRef<VirtualCallTarget> TargetsForSlot,
15400b57cec5SDimitry Andric ArrayRef<uint64_t> Args) {
15410b57cec5SDimitry Andric // Evaluate each function and store the result in each target's RetVal
15420b57cec5SDimitry Andric // field.
15430b57cec5SDimitry Andric for (VirtualCallTarget &Target : TargetsForSlot) {
154406c3fb27SDimitry Andric // TODO: Skip for now if the vtable symbol was an alias to a function,
154506c3fb27SDimitry Andric // need to evaluate whether it would be correct to analyze the aliasee
154606c3fb27SDimitry Andric // function for this optimization.
154706c3fb27SDimitry Andric auto Fn = dyn_cast<Function>(Target.Fn);
154806c3fb27SDimitry Andric if (!Fn)
154906c3fb27SDimitry Andric return false;
155006c3fb27SDimitry Andric
155106c3fb27SDimitry Andric if (Fn->arg_size() != Args.size() + 1)
15520b57cec5SDimitry Andric return false;
15530b57cec5SDimitry Andric
15540b57cec5SDimitry Andric Evaluator Eval(M.getDataLayout(), nullptr);
15550b57cec5SDimitry Andric SmallVector<Constant *, 2> EvalArgs;
15560b57cec5SDimitry Andric EvalArgs.push_back(
155706c3fb27SDimitry Andric Constant::getNullValue(Fn->getFunctionType()->getParamType(0)));
15580b57cec5SDimitry Andric for (unsigned I = 0; I != Args.size(); ++I) {
155906c3fb27SDimitry Andric auto *ArgTy =
156006c3fb27SDimitry Andric dyn_cast<IntegerType>(Fn->getFunctionType()->getParamType(I + 1));
15610b57cec5SDimitry Andric if (!ArgTy)
15620b57cec5SDimitry Andric return false;
15630b57cec5SDimitry Andric EvalArgs.push_back(ConstantInt::get(ArgTy, Args[I]));
15640b57cec5SDimitry Andric }
15650b57cec5SDimitry Andric
15660b57cec5SDimitry Andric Constant *RetVal;
156706c3fb27SDimitry Andric if (!Eval.EvaluateFunction(Fn, RetVal, EvalArgs) ||
15680b57cec5SDimitry Andric !isa<ConstantInt>(RetVal))
15690b57cec5SDimitry Andric return false;
15700b57cec5SDimitry Andric Target.RetVal = cast<ConstantInt>(RetVal)->getZExtValue();
15710b57cec5SDimitry Andric }
15720b57cec5SDimitry Andric return true;
15730b57cec5SDimitry Andric }
15740b57cec5SDimitry Andric
applyUniformRetValOpt(CallSiteInfo & CSInfo,StringRef FnName,uint64_t TheRetVal)15750b57cec5SDimitry Andric void DevirtModule::applyUniformRetValOpt(CallSiteInfo &CSInfo, StringRef FnName,
15760b57cec5SDimitry Andric uint64_t TheRetVal) {
1577fe6060f1SDimitry Andric for (auto Call : CSInfo.CallSites) {
1578fe6060f1SDimitry Andric if (!OptimizedCalls.insert(&Call.CB).second)
1579fe6060f1SDimitry Andric continue;
158081ad6265SDimitry Andric NumUniformRetVal++;
15810b57cec5SDimitry Andric Call.replaceAndErase(
15820b57cec5SDimitry Andric "uniform-ret-val", FnName, RemarksEnabled, OREGetter,
15835ffd83dbSDimitry Andric ConstantInt::get(cast<IntegerType>(Call.CB.getType()), TheRetVal));
1584fe6060f1SDimitry Andric }
15850b57cec5SDimitry Andric CSInfo.markDevirt();
15860b57cec5SDimitry Andric }
15870b57cec5SDimitry Andric
tryUniformRetValOpt(MutableArrayRef<VirtualCallTarget> TargetsForSlot,CallSiteInfo & CSInfo,WholeProgramDevirtResolution::ByArg * Res)15880b57cec5SDimitry Andric bool DevirtModule::tryUniformRetValOpt(
15890b57cec5SDimitry Andric MutableArrayRef<VirtualCallTarget> TargetsForSlot, CallSiteInfo &CSInfo,
15900b57cec5SDimitry Andric WholeProgramDevirtResolution::ByArg *Res) {
15910b57cec5SDimitry Andric // Uniform return value optimization. If all functions return the same
15920b57cec5SDimitry Andric // constant, replace all calls with that constant.
15930b57cec5SDimitry Andric uint64_t TheRetVal = TargetsForSlot[0].RetVal;
15940b57cec5SDimitry Andric for (const VirtualCallTarget &Target : TargetsForSlot)
15950b57cec5SDimitry Andric if (Target.RetVal != TheRetVal)
15960b57cec5SDimitry Andric return false;
15970b57cec5SDimitry Andric
15980b57cec5SDimitry Andric if (CSInfo.isExported()) {
15990b57cec5SDimitry Andric Res->TheKind = WholeProgramDevirtResolution::ByArg::UniformRetVal;
16000b57cec5SDimitry Andric Res->Info = TheRetVal;
16010b57cec5SDimitry Andric }
16020b57cec5SDimitry Andric
16030b57cec5SDimitry Andric applyUniformRetValOpt(CSInfo, TargetsForSlot[0].Fn->getName(), TheRetVal);
160481ad6265SDimitry Andric if (RemarksEnabled || AreStatisticsEnabled())
16050b57cec5SDimitry Andric for (auto &&Target : TargetsForSlot)
16060b57cec5SDimitry Andric Target.WasDevirt = true;
16070b57cec5SDimitry Andric return true;
16080b57cec5SDimitry Andric }
16090b57cec5SDimitry Andric
getGlobalName(VTableSlot Slot,ArrayRef<uint64_t> Args,StringRef Name)16100b57cec5SDimitry Andric std::string DevirtModule::getGlobalName(VTableSlot Slot,
16110b57cec5SDimitry Andric ArrayRef<uint64_t> Args,
16120b57cec5SDimitry Andric StringRef Name) {
16130b57cec5SDimitry Andric std::string FullName = "__typeid_";
16140b57cec5SDimitry Andric raw_string_ostream OS(FullName);
16150b57cec5SDimitry Andric OS << cast<MDString>(Slot.TypeID)->getString() << '_' << Slot.ByteOffset;
16160b57cec5SDimitry Andric for (uint64_t Arg : Args)
16170b57cec5SDimitry Andric OS << '_' << Arg;
16180b57cec5SDimitry Andric OS << '_' << Name;
1619*0fca6ea1SDimitry Andric return FullName;
16200b57cec5SDimitry Andric }
16210b57cec5SDimitry Andric
shouldExportConstantsAsAbsoluteSymbols()16220b57cec5SDimitry Andric bool DevirtModule::shouldExportConstantsAsAbsoluteSymbols() {
16230b57cec5SDimitry Andric Triple T(M.getTargetTriple());
1624480093f4SDimitry Andric return T.isX86() && T.getObjectFormat() == Triple::ELF;
16250b57cec5SDimitry Andric }
16260b57cec5SDimitry Andric
exportGlobal(VTableSlot Slot,ArrayRef<uint64_t> Args,StringRef Name,Constant * C)16270b57cec5SDimitry Andric void DevirtModule::exportGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args,
16280b57cec5SDimitry Andric StringRef Name, Constant *C) {
16290b57cec5SDimitry Andric GlobalAlias *GA = GlobalAlias::create(Int8Ty, 0, GlobalValue::ExternalLinkage,
16300b57cec5SDimitry Andric getGlobalName(Slot, Args, Name), C, &M);
16310b57cec5SDimitry Andric GA->setVisibility(GlobalValue::HiddenVisibility);
16320b57cec5SDimitry Andric }
16330b57cec5SDimitry Andric
exportConstant(VTableSlot Slot,ArrayRef<uint64_t> Args,StringRef Name,uint32_t Const,uint32_t & Storage)16340b57cec5SDimitry Andric void DevirtModule::exportConstant(VTableSlot Slot, ArrayRef<uint64_t> Args,
16350b57cec5SDimitry Andric StringRef Name, uint32_t Const,
16360b57cec5SDimitry Andric uint32_t &Storage) {
16370b57cec5SDimitry Andric if (shouldExportConstantsAsAbsoluteSymbols()) {
16380b57cec5SDimitry Andric exportGlobal(
16390b57cec5SDimitry Andric Slot, Args, Name,
16400b57cec5SDimitry Andric ConstantExpr::getIntToPtr(ConstantInt::get(Int32Ty, Const), Int8PtrTy));
16410b57cec5SDimitry Andric return;
16420b57cec5SDimitry Andric }
16430b57cec5SDimitry Andric
16440b57cec5SDimitry Andric Storage = Const;
16450b57cec5SDimitry Andric }
16460b57cec5SDimitry Andric
importGlobal(VTableSlot Slot,ArrayRef<uint64_t> Args,StringRef Name)16470b57cec5SDimitry Andric Constant *DevirtModule::importGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args,
16480b57cec5SDimitry Andric StringRef Name) {
16495ffd83dbSDimitry Andric Constant *C =
16505ffd83dbSDimitry Andric M.getOrInsertGlobal(getGlobalName(Slot, Args, Name), Int8Arr0Ty);
16510b57cec5SDimitry Andric auto *GV = dyn_cast<GlobalVariable>(C);
16520b57cec5SDimitry Andric if (GV)
16530b57cec5SDimitry Andric GV->setVisibility(GlobalValue::HiddenVisibility);
16540b57cec5SDimitry Andric return C;
16550b57cec5SDimitry Andric }
16560b57cec5SDimitry Andric
importConstant(VTableSlot Slot,ArrayRef<uint64_t> Args,StringRef Name,IntegerType * IntTy,uint32_t Storage)16570b57cec5SDimitry Andric Constant *DevirtModule::importConstant(VTableSlot Slot, ArrayRef<uint64_t> Args,
16580b57cec5SDimitry Andric StringRef Name, IntegerType *IntTy,
16590b57cec5SDimitry Andric uint32_t Storage) {
16600b57cec5SDimitry Andric if (!shouldExportConstantsAsAbsoluteSymbols())
16610b57cec5SDimitry Andric return ConstantInt::get(IntTy, Storage);
16620b57cec5SDimitry Andric
16630b57cec5SDimitry Andric Constant *C = importGlobal(Slot, Args, Name);
16640b57cec5SDimitry Andric auto *GV = cast<GlobalVariable>(C->stripPointerCasts());
16650b57cec5SDimitry Andric C = ConstantExpr::getPtrToInt(C, IntTy);
16660b57cec5SDimitry Andric
16670b57cec5SDimitry Andric // We only need to set metadata if the global is newly created, in which
16680b57cec5SDimitry Andric // case it would not have hidden visibility.
16690b57cec5SDimitry Andric if (GV->hasMetadata(LLVMContext::MD_absolute_symbol))
16700b57cec5SDimitry Andric return C;
16710b57cec5SDimitry Andric
16720b57cec5SDimitry Andric auto SetAbsRange = [&](uint64_t Min, uint64_t Max) {
16730b57cec5SDimitry Andric auto *MinC = ConstantAsMetadata::get(ConstantInt::get(IntPtrTy, Min));
16740b57cec5SDimitry Andric auto *MaxC = ConstantAsMetadata::get(ConstantInt::get(IntPtrTy, Max));
16750b57cec5SDimitry Andric GV->setMetadata(LLVMContext::MD_absolute_symbol,
16760b57cec5SDimitry Andric MDNode::get(M.getContext(), {MinC, MaxC}));
16770b57cec5SDimitry Andric };
16780b57cec5SDimitry Andric unsigned AbsWidth = IntTy->getBitWidth();
16790b57cec5SDimitry Andric if (AbsWidth == IntPtrTy->getBitWidth())
16800b57cec5SDimitry Andric SetAbsRange(~0ull, ~0ull); // Full set.
16810b57cec5SDimitry Andric else
16820b57cec5SDimitry Andric SetAbsRange(0, 1ull << AbsWidth);
16830b57cec5SDimitry Andric return C;
16840b57cec5SDimitry Andric }
16850b57cec5SDimitry Andric
applyUniqueRetValOpt(CallSiteInfo & CSInfo,StringRef FnName,bool IsOne,Constant * UniqueMemberAddr)16860b57cec5SDimitry Andric void DevirtModule::applyUniqueRetValOpt(CallSiteInfo &CSInfo, StringRef FnName,
16870b57cec5SDimitry Andric bool IsOne,
16880b57cec5SDimitry Andric Constant *UniqueMemberAddr) {
16890b57cec5SDimitry Andric for (auto &&Call : CSInfo.CallSites) {
1690fe6060f1SDimitry Andric if (!OptimizedCalls.insert(&Call.CB).second)
1691fe6060f1SDimitry Andric continue;
16925ffd83dbSDimitry Andric IRBuilder<> B(&Call.CB);
16930b57cec5SDimitry Andric Value *Cmp =
16945ffd83dbSDimitry Andric B.CreateICmp(IsOne ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE, Call.VTable,
16955ffd83dbSDimitry Andric B.CreateBitCast(UniqueMemberAddr, Call.VTable->getType()));
16965ffd83dbSDimitry Andric Cmp = B.CreateZExt(Cmp, Call.CB.getType());
169781ad6265SDimitry Andric NumUniqueRetVal++;
16980b57cec5SDimitry Andric Call.replaceAndErase("unique-ret-val", FnName, RemarksEnabled, OREGetter,
16990b57cec5SDimitry Andric Cmp);
17000b57cec5SDimitry Andric }
17010b57cec5SDimitry Andric CSInfo.markDevirt();
17020b57cec5SDimitry Andric }
17030b57cec5SDimitry Andric
getMemberAddr(const TypeMemberInfo * M)17040b57cec5SDimitry Andric Constant *DevirtModule::getMemberAddr(const TypeMemberInfo *M) {
17055f757f3fSDimitry Andric return ConstantExpr::getGetElementPtr(Int8Ty, M->Bits->GV,
17060b57cec5SDimitry Andric ConstantInt::get(Int64Ty, M->Offset));
17070b57cec5SDimitry Andric }
17080b57cec5SDimitry Andric
tryUniqueRetValOpt(unsigned BitWidth,MutableArrayRef<VirtualCallTarget> TargetsForSlot,CallSiteInfo & CSInfo,WholeProgramDevirtResolution::ByArg * Res,VTableSlot Slot,ArrayRef<uint64_t> Args)17090b57cec5SDimitry Andric bool DevirtModule::tryUniqueRetValOpt(
17100b57cec5SDimitry Andric unsigned BitWidth, MutableArrayRef<VirtualCallTarget> TargetsForSlot,
17110b57cec5SDimitry Andric CallSiteInfo &CSInfo, WholeProgramDevirtResolution::ByArg *Res,
17120b57cec5SDimitry Andric VTableSlot Slot, ArrayRef<uint64_t> Args) {
17130b57cec5SDimitry Andric // IsOne controls whether we look for a 0 or a 1.
17140b57cec5SDimitry Andric auto tryUniqueRetValOptFor = [&](bool IsOne) {
17150b57cec5SDimitry Andric const TypeMemberInfo *UniqueMember = nullptr;
17160b57cec5SDimitry Andric for (const VirtualCallTarget &Target : TargetsForSlot) {
17170b57cec5SDimitry Andric if (Target.RetVal == (IsOne ? 1 : 0)) {
17180b57cec5SDimitry Andric if (UniqueMember)
17190b57cec5SDimitry Andric return false;
17200b57cec5SDimitry Andric UniqueMember = Target.TM;
17210b57cec5SDimitry Andric }
17220b57cec5SDimitry Andric }
17230b57cec5SDimitry Andric
17240b57cec5SDimitry Andric // We should have found a unique member or bailed out by now. We already
17250b57cec5SDimitry Andric // checked for a uniform return value in tryUniformRetValOpt.
17260b57cec5SDimitry Andric assert(UniqueMember);
17270b57cec5SDimitry Andric
17280b57cec5SDimitry Andric Constant *UniqueMemberAddr = getMemberAddr(UniqueMember);
17290b57cec5SDimitry Andric if (CSInfo.isExported()) {
17300b57cec5SDimitry Andric Res->TheKind = WholeProgramDevirtResolution::ByArg::UniqueRetVal;
17310b57cec5SDimitry Andric Res->Info = IsOne;
17320b57cec5SDimitry Andric
17330b57cec5SDimitry Andric exportGlobal(Slot, Args, "unique_member", UniqueMemberAddr);
17340b57cec5SDimitry Andric }
17350b57cec5SDimitry Andric
17360b57cec5SDimitry Andric // Replace each call with the comparison.
17370b57cec5SDimitry Andric applyUniqueRetValOpt(CSInfo, TargetsForSlot[0].Fn->getName(), IsOne,
17380b57cec5SDimitry Andric UniqueMemberAddr);
17390b57cec5SDimitry Andric
17400b57cec5SDimitry Andric // Update devirtualization statistics for targets.
174181ad6265SDimitry Andric if (RemarksEnabled || AreStatisticsEnabled())
17420b57cec5SDimitry Andric for (auto &&Target : TargetsForSlot)
17430b57cec5SDimitry Andric Target.WasDevirt = true;
17440b57cec5SDimitry Andric
17450b57cec5SDimitry Andric return true;
17460b57cec5SDimitry Andric };
17470b57cec5SDimitry Andric
17480b57cec5SDimitry Andric if (BitWidth == 1) {
17490b57cec5SDimitry Andric if (tryUniqueRetValOptFor(true))
17500b57cec5SDimitry Andric return true;
17510b57cec5SDimitry Andric if (tryUniqueRetValOptFor(false))
17520b57cec5SDimitry Andric return true;
17530b57cec5SDimitry Andric }
17540b57cec5SDimitry Andric return false;
17550b57cec5SDimitry Andric }
17560b57cec5SDimitry Andric
applyVirtualConstProp(CallSiteInfo & CSInfo,StringRef FnName,Constant * Byte,Constant * Bit)17570b57cec5SDimitry Andric void DevirtModule::applyVirtualConstProp(CallSiteInfo &CSInfo, StringRef FnName,
17580b57cec5SDimitry Andric Constant *Byte, Constant *Bit) {
17590b57cec5SDimitry Andric for (auto Call : CSInfo.CallSites) {
1760fe6060f1SDimitry Andric if (!OptimizedCalls.insert(&Call.CB).second)
1761fe6060f1SDimitry Andric continue;
17625ffd83dbSDimitry Andric auto *RetType = cast<IntegerType>(Call.CB.getType());
17635ffd83dbSDimitry Andric IRBuilder<> B(&Call.CB);
17647a6dacacSDimitry Andric Value *Addr = B.CreatePtrAdd(Call.VTable, Byte);
17650b57cec5SDimitry Andric if (RetType->getBitWidth() == 1) {
17660b57cec5SDimitry Andric Value *Bits = B.CreateLoad(Int8Ty, Addr);
17670b57cec5SDimitry Andric Value *BitsAndBit = B.CreateAnd(Bits, Bit);
17680b57cec5SDimitry Andric auto IsBitSet = B.CreateICmpNE(BitsAndBit, ConstantInt::get(Int8Ty, 0));
176981ad6265SDimitry Andric NumVirtConstProp1Bit++;
17700b57cec5SDimitry Andric Call.replaceAndErase("virtual-const-prop-1-bit", FnName, RemarksEnabled,
17710b57cec5SDimitry Andric OREGetter, IsBitSet);
17720b57cec5SDimitry Andric } else {
177306c3fb27SDimitry Andric Value *Val = B.CreateLoad(RetType, Addr);
177481ad6265SDimitry Andric NumVirtConstProp++;
17750b57cec5SDimitry Andric Call.replaceAndErase("virtual-const-prop", FnName, RemarksEnabled,
17760b57cec5SDimitry Andric OREGetter, Val);
17770b57cec5SDimitry Andric }
17780b57cec5SDimitry Andric }
17790b57cec5SDimitry Andric CSInfo.markDevirt();
17800b57cec5SDimitry Andric }
17810b57cec5SDimitry Andric
tryVirtualConstProp(MutableArrayRef<VirtualCallTarget> TargetsForSlot,VTableSlotInfo & SlotInfo,WholeProgramDevirtResolution * Res,VTableSlot Slot)17820b57cec5SDimitry Andric bool DevirtModule::tryVirtualConstProp(
17830b57cec5SDimitry Andric MutableArrayRef<VirtualCallTarget> TargetsForSlot, VTableSlotInfo &SlotInfo,
17840b57cec5SDimitry Andric WholeProgramDevirtResolution *Res, VTableSlot Slot) {
178506c3fb27SDimitry Andric // TODO: Skip for now if the vtable symbol was an alias to a function,
178606c3fb27SDimitry Andric // need to evaluate whether it would be correct to analyze the aliasee
178706c3fb27SDimitry Andric // function for this optimization.
178806c3fb27SDimitry Andric auto Fn = dyn_cast<Function>(TargetsForSlot[0].Fn);
178906c3fb27SDimitry Andric if (!Fn)
179006c3fb27SDimitry Andric return false;
17910b57cec5SDimitry Andric // This only works if the function returns an integer.
179206c3fb27SDimitry Andric auto RetType = dyn_cast<IntegerType>(Fn->getReturnType());
17930b57cec5SDimitry Andric if (!RetType)
17940b57cec5SDimitry Andric return false;
17950b57cec5SDimitry Andric unsigned BitWidth = RetType->getBitWidth();
17960b57cec5SDimitry Andric if (BitWidth > 64)
17970b57cec5SDimitry Andric return false;
17980b57cec5SDimitry Andric
17990b57cec5SDimitry Andric // Make sure that each function is defined, does not access memory, takes at
18000b57cec5SDimitry Andric // least one argument, does not use its first argument (which we assume is
18010b57cec5SDimitry Andric // 'this'), and has the same return type.
18020b57cec5SDimitry Andric //
18030b57cec5SDimitry Andric // Note that we test whether this copy of the function is readnone, rather
18040b57cec5SDimitry Andric // than testing function attributes, which must hold for any copy of the
18050b57cec5SDimitry Andric // function, even a less optimized version substituted at link time. This is
18060b57cec5SDimitry Andric // sound because the virtual constant propagation optimizations effectively
18070b57cec5SDimitry Andric // inline all implementations of the virtual function into each call site,
18080b57cec5SDimitry Andric // rather than using function attributes to perform local optimization.
18090b57cec5SDimitry Andric for (VirtualCallTarget &Target : TargetsForSlot) {
181006c3fb27SDimitry Andric // TODO: Skip for now if the vtable symbol was an alias to a function,
181106c3fb27SDimitry Andric // need to evaluate whether it would be correct to analyze the aliasee
181206c3fb27SDimitry Andric // function for this optimization.
181306c3fb27SDimitry Andric auto Fn = dyn_cast<Function>(Target.Fn);
181406c3fb27SDimitry Andric if (!Fn)
181506c3fb27SDimitry Andric return false;
181606c3fb27SDimitry Andric
181706c3fb27SDimitry Andric if (Fn->isDeclaration() ||
181806c3fb27SDimitry Andric !computeFunctionBodyMemoryAccess(*Fn, AARGetter(*Fn))
1819bdd1243dSDimitry Andric .doesNotAccessMemory() ||
182006c3fb27SDimitry Andric Fn->arg_empty() || !Fn->arg_begin()->use_empty() ||
182106c3fb27SDimitry Andric Fn->getReturnType() != RetType)
18220b57cec5SDimitry Andric return false;
18230b57cec5SDimitry Andric }
18240b57cec5SDimitry Andric
18250b57cec5SDimitry Andric for (auto &&CSByConstantArg : SlotInfo.ConstCSInfo) {
18260b57cec5SDimitry Andric if (!tryEvaluateFunctionsWithArgs(TargetsForSlot, CSByConstantArg.first))
18270b57cec5SDimitry Andric continue;
18280b57cec5SDimitry Andric
18290b57cec5SDimitry Andric WholeProgramDevirtResolution::ByArg *ResByArg = nullptr;
18300b57cec5SDimitry Andric if (Res)
18310b57cec5SDimitry Andric ResByArg = &Res->ResByArg[CSByConstantArg.first];
18320b57cec5SDimitry Andric
18330b57cec5SDimitry Andric if (tryUniformRetValOpt(TargetsForSlot, CSByConstantArg.second, ResByArg))
18340b57cec5SDimitry Andric continue;
18350b57cec5SDimitry Andric
18360b57cec5SDimitry Andric if (tryUniqueRetValOpt(BitWidth, TargetsForSlot, CSByConstantArg.second,
18370b57cec5SDimitry Andric ResByArg, Slot, CSByConstantArg.first))
18380b57cec5SDimitry Andric continue;
18390b57cec5SDimitry Andric
18400b57cec5SDimitry Andric // Find an allocation offset in bits in all vtables associated with the
18410b57cec5SDimitry Andric // type.
18420b57cec5SDimitry Andric uint64_t AllocBefore =
18430b57cec5SDimitry Andric findLowestOffset(TargetsForSlot, /*IsAfter=*/false, BitWidth);
18440b57cec5SDimitry Andric uint64_t AllocAfter =
18450b57cec5SDimitry Andric findLowestOffset(TargetsForSlot, /*IsAfter=*/true, BitWidth);
18460b57cec5SDimitry Andric
18470b57cec5SDimitry Andric // Calculate the total amount of padding needed to store a value at both
18480b57cec5SDimitry Andric // ends of the object.
18490b57cec5SDimitry Andric uint64_t TotalPaddingBefore = 0, TotalPaddingAfter = 0;
18500b57cec5SDimitry Andric for (auto &&Target : TargetsForSlot) {
18510b57cec5SDimitry Andric TotalPaddingBefore += std::max<int64_t>(
18520b57cec5SDimitry Andric (AllocBefore + 7) / 8 - Target.allocatedBeforeBytes() - 1, 0);
18530b57cec5SDimitry Andric TotalPaddingAfter += std::max<int64_t>(
18540b57cec5SDimitry Andric (AllocAfter + 7) / 8 - Target.allocatedAfterBytes() - 1, 0);
18550b57cec5SDimitry Andric }
18560b57cec5SDimitry Andric
18570b57cec5SDimitry Andric // If the amount of padding is too large, give up.
18580b57cec5SDimitry Andric // FIXME: do something smarter here.
18590b57cec5SDimitry Andric if (std::min(TotalPaddingBefore, TotalPaddingAfter) > 128)
18600b57cec5SDimitry Andric continue;
18610b57cec5SDimitry Andric
18620b57cec5SDimitry Andric // Calculate the offset to the value as a (possibly negative) byte offset
18630b57cec5SDimitry Andric // and (if applicable) a bit offset, and store the values in the targets.
18640b57cec5SDimitry Andric int64_t OffsetByte;
18650b57cec5SDimitry Andric uint64_t OffsetBit;
18660b57cec5SDimitry Andric if (TotalPaddingBefore <= TotalPaddingAfter)
18670b57cec5SDimitry Andric setBeforeReturnValues(TargetsForSlot, AllocBefore, BitWidth, OffsetByte,
18680b57cec5SDimitry Andric OffsetBit);
18690b57cec5SDimitry Andric else
18700b57cec5SDimitry Andric setAfterReturnValues(TargetsForSlot, AllocAfter, BitWidth, OffsetByte,
18710b57cec5SDimitry Andric OffsetBit);
18720b57cec5SDimitry Andric
187381ad6265SDimitry Andric if (RemarksEnabled || AreStatisticsEnabled())
18740b57cec5SDimitry Andric for (auto &&Target : TargetsForSlot)
18750b57cec5SDimitry Andric Target.WasDevirt = true;
18760b57cec5SDimitry Andric
18770b57cec5SDimitry Andric
18780b57cec5SDimitry Andric if (CSByConstantArg.second.isExported()) {
18790b57cec5SDimitry Andric ResByArg->TheKind = WholeProgramDevirtResolution::ByArg::VirtualConstProp;
18800b57cec5SDimitry Andric exportConstant(Slot, CSByConstantArg.first, "byte", OffsetByte,
18810b57cec5SDimitry Andric ResByArg->Byte);
18820b57cec5SDimitry Andric exportConstant(Slot, CSByConstantArg.first, "bit", 1ULL << OffsetBit,
18830b57cec5SDimitry Andric ResByArg->Bit);
18840b57cec5SDimitry Andric }
18850b57cec5SDimitry Andric
18860b57cec5SDimitry Andric // Rewrite each call to a load from OffsetByte/OffsetBit.
18870b57cec5SDimitry Andric Constant *ByteConst = ConstantInt::get(Int32Ty, OffsetByte);
18880b57cec5SDimitry Andric Constant *BitConst = ConstantInt::get(Int8Ty, 1ULL << OffsetBit);
18890b57cec5SDimitry Andric applyVirtualConstProp(CSByConstantArg.second,
18900b57cec5SDimitry Andric TargetsForSlot[0].Fn->getName(), ByteConst, BitConst);
18910b57cec5SDimitry Andric }
18920b57cec5SDimitry Andric return true;
18930b57cec5SDimitry Andric }
18940b57cec5SDimitry Andric
rebuildGlobal(VTableBits & B)18950b57cec5SDimitry Andric void DevirtModule::rebuildGlobal(VTableBits &B) {
18960b57cec5SDimitry Andric if (B.Before.Bytes.empty() && B.After.Bytes.empty())
18970b57cec5SDimitry Andric return;
18980b57cec5SDimitry Andric
18998bcb0991SDimitry Andric // Align the before byte array to the global's minimum alignment so that we
19008bcb0991SDimitry Andric // don't break any alignment requirements on the global.
19015ffd83dbSDimitry Andric Align Alignment = M.getDataLayout().getValueOrABITypeAlignment(
19025ffd83dbSDimitry Andric B.GV->getAlign(), B.GV->getValueType());
19038bcb0991SDimitry Andric B.Before.Bytes.resize(alignTo(B.Before.Bytes.size(), Alignment));
19040b57cec5SDimitry Andric
19050b57cec5SDimitry Andric // Before was stored in reverse order; flip it now.
19060b57cec5SDimitry Andric for (size_t I = 0, Size = B.Before.Bytes.size(); I != Size / 2; ++I)
19070b57cec5SDimitry Andric std::swap(B.Before.Bytes[I], B.Before.Bytes[Size - 1 - I]);
19080b57cec5SDimitry Andric
19090b57cec5SDimitry Andric // Build an anonymous global containing the before bytes, followed by the
19100b57cec5SDimitry Andric // original initializer, followed by the after bytes.
19110b57cec5SDimitry Andric auto NewInit = ConstantStruct::getAnon(
19120b57cec5SDimitry Andric {ConstantDataArray::get(M.getContext(), B.Before.Bytes),
19130b57cec5SDimitry Andric B.GV->getInitializer(),
19140b57cec5SDimitry Andric ConstantDataArray::get(M.getContext(), B.After.Bytes)});
19150b57cec5SDimitry Andric auto NewGV =
19160b57cec5SDimitry Andric new GlobalVariable(M, NewInit->getType(), B.GV->isConstant(),
19170b57cec5SDimitry Andric GlobalVariable::PrivateLinkage, NewInit, "", B.GV);
19180b57cec5SDimitry Andric NewGV->setSection(B.GV->getSection());
19190b57cec5SDimitry Andric NewGV->setComdat(B.GV->getComdat());
19200eae32dcSDimitry Andric NewGV->setAlignment(B.GV->getAlign());
19210b57cec5SDimitry Andric
19220b57cec5SDimitry Andric // Copy the original vtable's metadata to the anonymous global, adjusting
19230b57cec5SDimitry Andric // offsets as required.
19240b57cec5SDimitry Andric NewGV->copyMetadata(B.GV, B.Before.Bytes.size());
19250b57cec5SDimitry Andric
19260b57cec5SDimitry Andric // Build an alias named after the original global, pointing at the second
19270b57cec5SDimitry Andric // element (the original initializer).
19280b57cec5SDimitry Andric auto Alias = GlobalAlias::create(
19290b57cec5SDimitry Andric B.GV->getInitializer()->getType(), 0, B.GV->getLinkage(), "",
1930*0fca6ea1SDimitry Andric ConstantExpr::getInBoundsGetElementPtr(
19310b57cec5SDimitry Andric NewInit->getType(), NewGV,
19320b57cec5SDimitry Andric ArrayRef<Constant *>{ConstantInt::get(Int32Ty, 0),
19330b57cec5SDimitry Andric ConstantInt::get(Int32Ty, 1)}),
19340b57cec5SDimitry Andric &M);
19350b57cec5SDimitry Andric Alias->setVisibility(B.GV->getVisibility());
19360b57cec5SDimitry Andric Alias->takeName(B.GV);
19370b57cec5SDimitry Andric
19380b57cec5SDimitry Andric B.GV->replaceAllUsesWith(Alias);
19390b57cec5SDimitry Andric B.GV->eraseFromParent();
19400b57cec5SDimitry Andric }
19410b57cec5SDimitry Andric
areRemarksEnabled()19420b57cec5SDimitry Andric bool DevirtModule::areRemarksEnabled() {
19430b57cec5SDimitry Andric const auto &FL = M.getFunctionList();
19440b57cec5SDimitry Andric for (const Function &Fn : FL) {
1945bdd1243dSDimitry Andric if (Fn.empty())
19460b57cec5SDimitry Andric continue;
1947bdd1243dSDimitry Andric auto DI = OptimizationRemark(DEBUG_TYPE, "", DebugLoc(), &Fn.front());
19480b57cec5SDimitry Andric return DI.isEnabled();
19490b57cec5SDimitry Andric }
19500b57cec5SDimitry Andric return false;
19510b57cec5SDimitry Andric }
19520b57cec5SDimitry Andric
scanTypeTestUsers(Function * TypeTestFunc,DenseMap<Metadata *,std::set<TypeMemberInfo>> & TypeIdMap)19535ffd83dbSDimitry Andric void DevirtModule::scanTypeTestUsers(
19545ffd83dbSDimitry Andric Function *TypeTestFunc,
19555ffd83dbSDimitry Andric DenseMap<Metadata *, std::set<TypeMemberInfo>> &TypeIdMap) {
19560b57cec5SDimitry Andric // Find all virtual calls via a virtual table pointer %p under an assumption
19570b57cec5SDimitry Andric // of the form llvm.assume(llvm.type.test(%p, %md)). This indicates that %p
19580b57cec5SDimitry Andric // points to a member of the type identifier %md. Group calls by (type ID,
19590b57cec5SDimitry Andric // offset) pair (effectively the identity of the virtual function) and store
19600b57cec5SDimitry Andric // to CallSlots.
1961349cc55cSDimitry Andric for (Use &U : llvm::make_early_inc_range(TypeTestFunc->uses())) {
1962349cc55cSDimitry Andric auto *CI = dyn_cast<CallInst>(U.getUser());
19630b57cec5SDimitry Andric if (!CI)
19640b57cec5SDimitry Andric continue;
19650b57cec5SDimitry Andric
19660b57cec5SDimitry Andric // Search for virtual calls based on %p and add them to DevirtCalls.
19670b57cec5SDimitry Andric SmallVector<DevirtCallSite, 1> DevirtCalls;
19680b57cec5SDimitry Andric SmallVector<CallInst *, 1> Assumes;
19690b57cec5SDimitry Andric auto &DT = LookupDomTree(*CI->getFunction());
19700b57cec5SDimitry Andric findDevirtualizableCallsForTypeTest(DevirtCalls, Assumes, CI, DT);
19710b57cec5SDimitry Andric
19720b57cec5SDimitry Andric Metadata *TypeId =
19730b57cec5SDimitry Andric cast<MetadataAsValue>(CI->getArgOperand(1))->getMetadata();
19745ffd83dbSDimitry Andric // If we found any, add them to CallSlots.
19755ffd83dbSDimitry Andric if (!Assumes.empty()) {
19760b57cec5SDimitry Andric Value *Ptr = CI->getArgOperand(0)->stripPointerCasts();
19775ffd83dbSDimitry Andric for (DevirtCallSite Call : DevirtCalls)
19785ffd83dbSDimitry Andric CallSlots[{TypeId, Call.Offset}].addCallSite(Ptr, Call.CB, nullptr);
19790b57cec5SDimitry Andric }
19800b57cec5SDimitry Andric
19815ffd83dbSDimitry Andric auto RemoveTypeTestAssumes = [&]() {
19820b57cec5SDimitry Andric // We no longer need the assumes or the type test.
1983bdd1243dSDimitry Andric for (auto *Assume : Assumes)
19840b57cec5SDimitry Andric Assume->eraseFromParent();
19850b57cec5SDimitry Andric // We can't use RecursivelyDeleteTriviallyDeadInstructions here because we
19860b57cec5SDimitry Andric // may use the vtable argument later.
19870b57cec5SDimitry Andric if (CI->use_empty())
19880b57cec5SDimitry Andric CI->eraseFromParent();
19895ffd83dbSDimitry Andric };
19905ffd83dbSDimitry Andric
19915ffd83dbSDimitry Andric // At this point we could remove all type test assume sequences, as they
19925ffd83dbSDimitry Andric // were originally inserted for WPD. However, we can keep these in the
19935ffd83dbSDimitry Andric // code stream for later analysis (e.g. to help drive more efficient ICP
19945ffd83dbSDimitry Andric // sequences). They will eventually be removed by a second LowerTypeTests
19955ffd83dbSDimitry Andric // invocation that cleans them up. In order to do this correctly, the first
19965ffd83dbSDimitry Andric // LowerTypeTests invocation needs to know that they have "Unknown" type
19975ffd83dbSDimitry Andric // test resolution, so that they aren't treated as Unsat and lowered to
19985ffd83dbSDimitry Andric // False, which will break any uses on assumes. Below we remove any type
19995ffd83dbSDimitry Andric // test assumes that will not be treated as Unknown by LTT.
20005ffd83dbSDimitry Andric
20015ffd83dbSDimitry Andric // The type test assumes will be treated by LTT as Unsat if the type id is
20025ffd83dbSDimitry Andric // not used on a global (in which case it has no entry in the TypeIdMap).
20035ffd83dbSDimitry Andric if (!TypeIdMap.count(TypeId))
20045ffd83dbSDimitry Andric RemoveTypeTestAssumes();
20055ffd83dbSDimitry Andric
20065ffd83dbSDimitry Andric // For ThinLTO importing, we need to remove the type test assumes if this is
20075ffd83dbSDimitry Andric // an MDString type id without a corresponding TypeIdSummary. Any
20085ffd83dbSDimitry Andric // non-MDString type ids are ignored and treated as Unknown by LTT, so their
20095ffd83dbSDimitry Andric // type test assumes can be kept. If the MDString type id is missing a
20105ffd83dbSDimitry Andric // TypeIdSummary (e.g. because there was no use on a vcall, preventing the
20115ffd83dbSDimitry Andric // exporting phase of WPD from analyzing it), then it would be treated as
20125ffd83dbSDimitry Andric // Unsat by LTT and we need to remove its type test assumes here. If not
20135ffd83dbSDimitry Andric // used on a vcall we don't need them for later optimization use in any
20145ffd83dbSDimitry Andric // case.
20155ffd83dbSDimitry Andric else if (ImportSummary && isa<MDString>(TypeId)) {
20165ffd83dbSDimitry Andric const TypeIdSummary *TidSummary =
20175ffd83dbSDimitry Andric ImportSummary->getTypeIdSummary(cast<MDString>(TypeId)->getString());
20185ffd83dbSDimitry Andric if (!TidSummary)
20195ffd83dbSDimitry Andric RemoveTypeTestAssumes();
20205ffd83dbSDimitry Andric else
20215ffd83dbSDimitry Andric // If one was created it should not be Unsat, because if we reached here
20225ffd83dbSDimitry Andric // the type id was used on a global.
20235ffd83dbSDimitry Andric assert(TidSummary->TTRes.TheKind != TypeTestResolution::Unsat);
20245ffd83dbSDimitry Andric }
20250b57cec5SDimitry Andric }
20260b57cec5SDimitry Andric }
20270b57cec5SDimitry Andric
scanTypeCheckedLoadUsers(Function * TypeCheckedLoadFunc)20280b57cec5SDimitry Andric void DevirtModule::scanTypeCheckedLoadUsers(Function *TypeCheckedLoadFunc) {
20290b57cec5SDimitry Andric Function *TypeTestFunc = Intrinsic::getDeclaration(&M, Intrinsic::type_test);
20300b57cec5SDimitry Andric
2031349cc55cSDimitry Andric for (Use &U : llvm::make_early_inc_range(TypeCheckedLoadFunc->uses())) {
2032349cc55cSDimitry Andric auto *CI = dyn_cast<CallInst>(U.getUser());
20330b57cec5SDimitry Andric if (!CI)
20340b57cec5SDimitry Andric continue;
20350b57cec5SDimitry Andric
20360b57cec5SDimitry Andric Value *Ptr = CI->getArgOperand(0);
20370b57cec5SDimitry Andric Value *Offset = CI->getArgOperand(1);
20380b57cec5SDimitry Andric Value *TypeIdValue = CI->getArgOperand(2);
20390b57cec5SDimitry Andric Metadata *TypeId = cast<MetadataAsValue>(TypeIdValue)->getMetadata();
20400b57cec5SDimitry Andric
20410b57cec5SDimitry Andric SmallVector<DevirtCallSite, 1> DevirtCalls;
20420b57cec5SDimitry Andric SmallVector<Instruction *, 1> LoadedPtrs;
20430b57cec5SDimitry Andric SmallVector<Instruction *, 1> Preds;
20440b57cec5SDimitry Andric bool HasNonCallUses = false;
20450b57cec5SDimitry Andric auto &DT = LookupDomTree(*CI->getFunction());
20460b57cec5SDimitry Andric findDevirtualizableCallsForTypeCheckedLoad(DevirtCalls, LoadedPtrs, Preds,
20470b57cec5SDimitry Andric HasNonCallUses, CI, DT);
20480b57cec5SDimitry Andric
20490b57cec5SDimitry Andric // Start by generating "pessimistic" code that explicitly loads the function
20500b57cec5SDimitry Andric // pointer from the vtable and performs the type check. If possible, we will
20510b57cec5SDimitry Andric // eliminate the load and the type check later.
20520b57cec5SDimitry Andric
20530b57cec5SDimitry Andric // If possible, only generate the load at the point where it is used.
20540b57cec5SDimitry Andric // This helps avoid unnecessary spills.
20550b57cec5SDimitry Andric IRBuilder<> LoadB(
20560b57cec5SDimitry Andric (LoadedPtrs.size() == 1 && !HasNonCallUses) ? LoadedPtrs[0] : CI);
205706c3fb27SDimitry Andric
205806c3fb27SDimitry Andric Value *LoadedValue = nullptr;
205906c3fb27SDimitry Andric if (TypeCheckedLoadFunc->getIntrinsicID() ==
206006c3fb27SDimitry Andric Intrinsic::type_checked_load_relative) {
20617a6dacacSDimitry Andric Value *GEP = LoadB.CreatePtrAdd(Ptr, Offset);
20625f757f3fSDimitry Andric LoadedValue = LoadB.CreateLoad(Int32Ty, GEP);
206306c3fb27SDimitry Andric LoadedValue = LoadB.CreateSExt(LoadedValue, IntPtrTy);
206406c3fb27SDimitry Andric GEP = LoadB.CreatePtrToInt(GEP, IntPtrTy);
206506c3fb27SDimitry Andric LoadedValue = LoadB.CreateAdd(GEP, LoadedValue);
206606c3fb27SDimitry Andric LoadedValue = LoadB.CreateIntToPtr(LoadedValue, Int8PtrTy);
206706c3fb27SDimitry Andric } else {
20687a6dacacSDimitry Andric Value *GEP = LoadB.CreatePtrAdd(Ptr, Offset);
20695f757f3fSDimitry Andric LoadedValue = LoadB.CreateLoad(Int8PtrTy, GEP);
207006c3fb27SDimitry Andric }
20710b57cec5SDimitry Andric
20720b57cec5SDimitry Andric for (Instruction *LoadedPtr : LoadedPtrs) {
20730b57cec5SDimitry Andric LoadedPtr->replaceAllUsesWith(LoadedValue);
20740b57cec5SDimitry Andric LoadedPtr->eraseFromParent();
20750b57cec5SDimitry Andric }
20760b57cec5SDimitry Andric
20770b57cec5SDimitry Andric // Likewise for the type test.
20780b57cec5SDimitry Andric IRBuilder<> CallB((Preds.size() == 1 && !HasNonCallUses) ? Preds[0] : CI);
20790b57cec5SDimitry Andric CallInst *TypeTestCall = CallB.CreateCall(TypeTestFunc, {Ptr, TypeIdValue});
20800b57cec5SDimitry Andric
20810b57cec5SDimitry Andric for (Instruction *Pred : Preds) {
20820b57cec5SDimitry Andric Pred->replaceAllUsesWith(TypeTestCall);
20830b57cec5SDimitry Andric Pred->eraseFromParent();
20840b57cec5SDimitry Andric }
20850b57cec5SDimitry Andric
20860b57cec5SDimitry Andric // We have already erased any extractvalue instructions that refer to the
20870b57cec5SDimitry Andric // intrinsic call, but the intrinsic may have other non-extractvalue uses
20880b57cec5SDimitry Andric // (although this is unlikely). In that case, explicitly build a pair and
20890b57cec5SDimitry Andric // RAUW it.
20900b57cec5SDimitry Andric if (!CI->use_empty()) {
209181ad6265SDimitry Andric Value *Pair = PoisonValue::get(CI->getType());
20920b57cec5SDimitry Andric IRBuilder<> B(CI);
20930b57cec5SDimitry Andric Pair = B.CreateInsertValue(Pair, LoadedValue, {0});
20940b57cec5SDimitry Andric Pair = B.CreateInsertValue(Pair, TypeTestCall, {1});
20950b57cec5SDimitry Andric CI->replaceAllUsesWith(Pair);
20960b57cec5SDimitry Andric }
20970b57cec5SDimitry Andric
20980b57cec5SDimitry Andric // The number of unsafe uses is initially the number of uses.
20990b57cec5SDimitry Andric auto &NumUnsafeUses = NumUnsafeUsesForTypeTest[TypeTestCall];
21000b57cec5SDimitry Andric NumUnsafeUses = DevirtCalls.size();
21010b57cec5SDimitry Andric
21020b57cec5SDimitry Andric // If the function pointer has a non-call user, we cannot eliminate the type
21030b57cec5SDimitry Andric // check, as one of those users may eventually call the pointer. Increment
21040b57cec5SDimitry Andric // the unsafe use count to make sure it cannot reach zero.
21050b57cec5SDimitry Andric if (HasNonCallUses)
21060b57cec5SDimitry Andric ++NumUnsafeUses;
21070b57cec5SDimitry Andric for (DevirtCallSite Call : DevirtCalls) {
21085ffd83dbSDimitry Andric CallSlots[{TypeId, Call.Offset}].addCallSite(Ptr, Call.CB,
21090b57cec5SDimitry Andric &NumUnsafeUses);
21100b57cec5SDimitry Andric }
21110b57cec5SDimitry Andric
21120b57cec5SDimitry Andric CI->eraseFromParent();
21130b57cec5SDimitry Andric }
21140b57cec5SDimitry Andric }
21150b57cec5SDimitry Andric
importResolution(VTableSlot Slot,VTableSlotInfo & SlotInfo)21160b57cec5SDimitry Andric void DevirtModule::importResolution(VTableSlot Slot, VTableSlotInfo &SlotInfo) {
21178bcb0991SDimitry Andric auto *TypeId = dyn_cast<MDString>(Slot.TypeID);
21188bcb0991SDimitry Andric if (!TypeId)
21198bcb0991SDimitry Andric return;
21200b57cec5SDimitry Andric const TypeIdSummary *TidSummary =
21218bcb0991SDimitry Andric ImportSummary->getTypeIdSummary(TypeId->getString());
21220b57cec5SDimitry Andric if (!TidSummary)
21230b57cec5SDimitry Andric return;
21240b57cec5SDimitry Andric auto ResI = TidSummary->WPDRes.find(Slot.ByteOffset);
21250b57cec5SDimitry Andric if (ResI == TidSummary->WPDRes.end())
21260b57cec5SDimitry Andric return;
21270b57cec5SDimitry Andric const WholeProgramDevirtResolution &Res = ResI->second;
21280b57cec5SDimitry Andric
21290b57cec5SDimitry Andric if (Res.TheKind == WholeProgramDevirtResolution::SingleImpl) {
21308bcb0991SDimitry Andric assert(!Res.SingleImplName.empty());
21310b57cec5SDimitry Andric // The type of the function in the declaration is irrelevant because every
21320b57cec5SDimitry Andric // call site will cast it to the correct type.
21330b57cec5SDimitry Andric Constant *SingleImpl =
21340b57cec5SDimitry Andric cast<Constant>(M.getOrInsertFunction(Res.SingleImplName,
21350b57cec5SDimitry Andric Type::getVoidTy(M.getContext()))
21360b57cec5SDimitry Andric .getCallee());
21370b57cec5SDimitry Andric
21380b57cec5SDimitry Andric // This is the import phase so we should not be exporting anything.
21390b57cec5SDimitry Andric bool IsExported = false;
21400b57cec5SDimitry Andric applySingleImplDevirt(SlotInfo, SingleImpl, IsExported);
21410b57cec5SDimitry Andric assert(!IsExported);
21420b57cec5SDimitry Andric }
21430b57cec5SDimitry Andric
21440b57cec5SDimitry Andric for (auto &CSByConstantArg : SlotInfo.ConstCSInfo) {
21450b57cec5SDimitry Andric auto I = Res.ResByArg.find(CSByConstantArg.first);
21460b57cec5SDimitry Andric if (I == Res.ResByArg.end())
21470b57cec5SDimitry Andric continue;
21480b57cec5SDimitry Andric auto &ResByArg = I->second;
21490b57cec5SDimitry Andric // FIXME: We should figure out what to do about the "function name" argument
21500b57cec5SDimitry Andric // to the apply* functions, as the function names are unavailable during the
21510b57cec5SDimitry Andric // importing phase. For now we just pass the empty string. This does not
21520b57cec5SDimitry Andric // impact correctness because the function names are just used for remarks.
21530b57cec5SDimitry Andric switch (ResByArg.TheKind) {
21540b57cec5SDimitry Andric case WholeProgramDevirtResolution::ByArg::UniformRetVal:
21550b57cec5SDimitry Andric applyUniformRetValOpt(CSByConstantArg.second, "", ResByArg.Info);
21560b57cec5SDimitry Andric break;
21570b57cec5SDimitry Andric case WholeProgramDevirtResolution::ByArg::UniqueRetVal: {
21580b57cec5SDimitry Andric Constant *UniqueMemberAddr =
21590b57cec5SDimitry Andric importGlobal(Slot, CSByConstantArg.first, "unique_member");
21600b57cec5SDimitry Andric applyUniqueRetValOpt(CSByConstantArg.second, "", ResByArg.Info,
21610b57cec5SDimitry Andric UniqueMemberAddr);
21620b57cec5SDimitry Andric break;
21630b57cec5SDimitry Andric }
21640b57cec5SDimitry Andric case WholeProgramDevirtResolution::ByArg::VirtualConstProp: {
21650b57cec5SDimitry Andric Constant *Byte = importConstant(Slot, CSByConstantArg.first, "byte",
21660b57cec5SDimitry Andric Int32Ty, ResByArg.Byte);
21670b57cec5SDimitry Andric Constant *Bit = importConstant(Slot, CSByConstantArg.first, "bit", Int8Ty,
21680b57cec5SDimitry Andric ResByArg.Bit);
21690b57cec5SDimitry Andric applyVirtualConstProp(CSByConstantArg.second, "", Byte, Bit);
21700b57cec5SDimitry Andric break;
21710b57cec5SDimitry Andric }
21720b57cec5SDimitry Andric default:
21730b57cec5SDimitry Andric break;
21740b57cec5SDimitry Andric }
21750b57cec5SDimitry Andric }
21760b57cec5SDimitry Andric
21770b57cec5SDimitry Andric if (Res.TheKind == WholeProgramDevirtResolution::BranchFunnel) {
21780b57cec5SDimitry Andric // The type of the function is irrelevant, because it's bitcast at calls
21790b57cec5SDimitry Andric // anyhow.
21800b57cec5SDimitry Andric Constant *JT = cast<Constant>(
21810b57cec5SDimitry Andric M.getOrInsertFunction(getGlobalName(Slot, {}, "branch_funnel"),
21820b57cec5SDimitry Andric Type::getVoidTy(M.getContext()))
21830b57cec5SDimitry Andric .getCallee());
21840b57cec5SDimitry Andric bool IsExported = false;
21850b57cec5SDimitry Andric applyICallBranchFunnel(SlotInfo, JT, IsExported);
21860b57cec5SDimitry Andric assert(!IsExported);
21870b57cec5SDimitry Andric }
21880b57cec5SDimitry Andric }
21890b57cec5SDimitry Andric
removeRedundantTypeTests()21900b57cec5SDimitry Andric void DevirtModule::removeRedundantTypeTests() {
21910b57cec5SDimitry Andric auto True = ConstantInt::getTrue(M.getContext());
21920b57cec5SDimitry Andric for (auto &&U : NumUnsafeUsesForTypeTest) {
21930b57cec5SDimitry Andric if (U.second == 0) {
21940b57cec5SDimitry Andric U.first->replaceAllUsesWith(True);
21950b57cec5SDimitry Andric U.first->eraseFromParent();
21960b57cec5SDimitry Andric }
21970b57cec5SDimitry Andric }
21980b57cec5SDimitry Andric }
21990b57cec5SDimitry Andric
22000eae32dcSDimitry Andric ValueInfo
lookUpFunctionValueInfo(Function * TheFn,ModuleSummaryIndex * ExportSummary)22010eae32dcSDimitry Andric DevirtModule::lookUpFunctionValueInfo(Function *TheFn,
22020eae32dcSDimitry Andric ModuleSummaryIndex *ExportSummary) {
22030eae32dcSDimitry Andric assert((ExportSummary != nullptr) &&
22040eae32dcSDimitry Andric "Caller guarantees ExportSummary is not nullptr");
22050eae32dcSDimitry Andric
22060eae32dcSDimitry Andric const auto TheFnGUID = TheFn->getGUID();
22070eae32dcSDimitry Andric const auto TheFnGUIDWithExportedName = GlobalValue::getGUID(TheFn->getName());
22080eae32dcSDimitry Andric // Look up ValueInfo with the GUID in the current linkage.
22090eae32dcSDimitry Andric ValueInfo TheFnVI = ExportSummary->getValueInfo(TheFnGUID);
22100eae32dcSDimitry Andric // If no entry is found and GUID is different from GUID computed using
22110eae32dcSDimitry Andric // exported name, look up ValueInfo with the exported name unconditionally.
22120eae32dcSDimitry Andric // This is a fallback.
22130eae32dcSDimitry Andric //
22140eae32dcSDimitry Andric // The reason to have a fallback:
22150eae32dcSDimitry Andric // 1. LTO could enable global value internalization via
22160eae32dcSDimitry Andric // `enable-lto-internalization`.
22170eae32dcSDimitry Andric // 2. The GUID in ExportedSummary is computed using exported name.
22180eae32dcSDimitry Andric if ((!TheFnVI) && (TheFnGUID != TheFnGUIDWithExportedName)) {
22190eae32dcSDimitry Andric TheFnVI = ExportSummary->getValueInfo(TheFnGUIDWithExportedName);
22200eae32dcSDimitry Andric }
22210eae32dcSDimitry Andric return TheFnVI;
22220eae32dcSDimitry Andric }
22230eae32dcSDimitry Andric
mustBeUnreachableFunction(Function * const F,ModuleSummaryIndex * ExportSummary)22240eae32dcSDimitry Andric bool DevirtModule::mustBeUnreachableFunction(
22250eae32dcSDimitry Andric Function *const F, ModuleSummaryIndex *ExportSummary) {
22260eae32dcSDimitry Andric // First, learn unreachability by analyzing function IR.
22270eae32dcSDimitry Andric if (!F->isDeclaration()) {
22280eae32dcSDimitry Andric // A function must be unreachable if its entry block ends with an
22290eae32dcSDimitry Andric // 'unreachable'.
22300eae32dcSDimitry Andric return isa<UnreachableInst>(F->getEntryBlock().getTerminator());
22310eae32dcSDimitry Andric }
22320eae32dcSDimitry Andric // Learn unreachability from ExportSummary if ExportSummary is present.
22330eae32dcSDimitry Andric return ExportSummary &&
22340eae32dcSDimitry Andric ::mustBeUnreachableFunction(
22350eae32dcSDimitry Andric DevirtModule::lookUpFunctionValueInfo(F, ExportSummary));
22360eae32dcSDimitry Andric }
22370eae32dcSDimitry Andric
run()22380b57cec5SDimitry Andric bool DevirtModule::run() {
22390b57cec5SDimitry Andric // If only some of the modules were split, we cannot correctly perform
22400b57cec5SDimitry Andric // this transformation. We already checked for the presense of type tests
22410b57cec5SDimitry Andric // with partially split modules during the thin link, and would have emitted
22420b57cec5SDimitry Andric // an error if any were found, so here we can simply return.
22430b57cec5SDimitry Andric if ((ExportSummary && ExportSummary->partiallySplitLTOUnits()) ||
22440b57cec5SDimitry Andric (ImportSummary && ImportSummary->partiallySplitLTOUnits()))
22450b57cec5SDimitry Andric return false;
22460b57cec5SDimitry Andric
22470b57cec5SDimitry Andric Function *TypeTestFunc =
22480b57cec5SDimitry Andric M.getFunction(Intrinsic::getName(Intrinsic::type_test));
22490b57cec5SDimitry Andric Function *TypeCheckedLoadFunc =
22500b57cec5SDimitry Andric M.getFunction(Intrinsic::getName(Intrinsic::type_checked_load));
225106c3fb27SDimitry Andric Function *TypeCheckedLoadRelativeFunc =
225206c3fb27SDimitry Andric M.getFunction(Intrinsic::getName(Intrinsic::type_checked_load_relative));
22530b57cec5SDimitry Andric Function *AssumeFunc = M.getFunction(Intrinsic::getName(Intrinsic::assume));
22540b57cec5SDimitry Andric
22550b57cec5SDimitry Andric // Normally if there are no users of the devirtualization intrinsics in the
22560b57cec5SDimitry Andric // module, this pass has nothing to do. But if we are exporting, we also need
22570b57cec5SDimitry Andric // to handle any users that appear only in the function summaries.
22580b57cec5SDimitry Andric if (!ExportSummary &&
22590b57cec5SDimitry Andric (!TypeTestFunc || TypeTestFunc->use_empty() || !AssumeFunc ||
22600b57cec5SDimitry Andric AssumeFunc->use_empty()) &&
226106c3fb27SDimitry Andric (!TypeCheckedLoadFunc || TypeCheckedLoadFunc->use_empty()) &&
226206c3fb27SDimitry Andric (!TypeCheckedLoadRelativeFunc ||
226306c3fb27SDimitry Andric TypeCheckedLoadRelativeFunc->use_empty()))
22640b57cec5SDimitry Andric return false;
22650b57cec5SDimitry Andric
22665ffd83dbSDimitry Andric // Rebuild type metadata into a map for easy lookup.
22675ffd83dbSDimitry Andric std::vector<VTableBits> Bits;
22685ffd83dbSDimitry Andric DenseMap<Metadata *, std::set<TypeMemberInfo>> TypeIdMap;
22695ffd83dbSDimitry Andric buildTypeIdentifierMap(Bits, TypeIdMap);
22705ffd83dbSDimitry Andric
22710b57cec5SDimitry Andric if (TypeTestFunc && AssumeFunc)
22725ffd83dbSDimitry Andric scanTypeTestUsers(TypeTestFunc, TypeIdMap);
22730b57cec5SDimitry Andric
22740b57cec5SDimitry Andric if (TypeCheckedLoadFunc)
22750b57cec5SDimitry Andric scanTypeCheckedLoadUsers(TypeCheckedLoadFunc);
22760b57cec5SDimitry Andric
227706c3fb27SDimitry Andric if (TypeCheckedLoadRelativeFunc)
227806c3fb27SDimitry Andric scanTypeCheckedLoadUsers(TypeCheckedLoadRelativeFunc);
227906c3fb27SDimitry Andric
22800b57cec5SDimitry Andric if (ImportSummary) {
22810b57cec5SDimitry Andric for (auto &S : CallSlots)
22820b57cec5SDimitry Andric importResolution(S.first, S.second);
22830b57cec5SDimitry Andric
22840b57cec5SDimitry Andric removeRedundantTypeTests();
22850b57cec5SDimitry Andric
228681ad6265SDimitry Andric // We have lowered or deleted the type intrinsics, so we will no longer have
228781ad6265SDimitry Andric // enough information to reason about the liveness of virtual function
228881ad6265SDimitry Andric // pointers in GlobalDCE.
22895ffd83dbSDimitry Andric for (GlobalVariable &GV : M.globals())
22905ffd83dbSDimitry Andric GV.eraseMetadata(LLVMContext::MD_vcall_visibility);
22915ffd83dbSDimitry Andric
22920b57cec5SDimitry Andric // The rest of the code is only necessary when exporting or during regular
22930b57cec5SDimitry Andric // LTO, so we are done.
22940b57cec5SDimitry Andric return true;
22950b57cec5SDimitry Andric }
22960b57cec5SDimitry Andric
22970b57cec5SDimitry Andric if (TypeIdMap.empty())
22980b57cec5SDimitry Andric return true;
22990b57cec5SDimitry Andric
23000b57cec5SDimitry Andric // Collect information from summary about which calls to try to devirtualize.
23010b57cec5SDimitry Andric if (ExportSummary) {
23020b57cec5SDimitry Andric DenseMap<GlobalValue::GUID, TinyPtrVector<Metadata *>> MetadataByGUID;
23030b57cec5SDimitry Andric for (auto &P : TypeIdMap) {
23040b57cec5SDimitry Andric if (auto *TypeId = dyn_cast<MDString>(P.first))
23050b57cec5SDimitry Andric MetadataByGUID[GlobalValue::getGUID(TypeId->getString())].push_back(
23060b57cec5SDimitry Andric TypeId);
23070b57cec5SDimitry Andric }
23080b57cec5SDimitry Andric
23090b57cec5SDimitry Andric for (auto &P : *ExportSummary) {
23100b57cec5SDimitry Andric for (auto &S : P.second.SummaryList) {
23110b57cec5SDimitry Andric auto *FS = dyn_cast<FunctionSummary>(S.get());
23120b57cec5SDimitry Andric if (!FS)
23130b57cec5SDimitry Andric continue;
23140b57cec5SDimitry Andric // FIXME: Only add live functions.
23150b57cec5SDimitry Andric for (FunctionSummary::VFuncId VF : FS->type_test_assume_vcalls()) {
23160b57cec5SDimitry Andric for (Metadata *MD : MetadataByGUID[VF.GUID]) {
23178bcb0991SDimitry Andric CallSlots[{MD, VF.Offset}].CSInfo.addSummaryTypeTestAssumeUser(FS);
23180b57cec5SDimitry Andric }
23190b57cec5SDimitry Andric }
23200b57cec5SDimitry Andric for (FunctionSummary::VFuncId VF : FS->type_checked_load_vcalls()) {
23210b57cec5SDimitry Andric for (Metadata *MD : MetadataByGUID[VF.GUID]) {
23220b57cec5SDimitry Andric CallSlots[{MD, VF.Offset}].CSInfo.addSummaryTypeCheckedLoadUser(FS);
23230b57cec5SDimitry Andric }
23240b57cec5SDimitry Andric }
23250b57cec5SDimitry Andric for (const FunctionSummary::ConstVCall &VC :
23260b57cec5SDimitry Andric FS->type_test_assume_const_vcalls()) {
23270b57cec5SDimitry Andric for (Metadata *MD : MetadataByGUID[VC.VFunc.GUID]) {
23280b57cec5SDimitry Andric CallSlots[{MD, VC.VFunc.Offset}]
23290b57cec5SDimitry Andric .ConstCSInfo[VC.Args]
23308bcb0991SDimitry Andric .addSummaryTypeTestAssumeUser(FS);
23310b57cec5SDimitry Andric }
23320b57cec5SDimitry Andric }
23330b57cec5SDimitry Andric for (const FunctionSummary::ConstVCall &VC :
23340b57cec5SDimitry Andric FS->type_checked_load_const_vcalls()) {
23350b57cec5SDimitry Andric for (Metadata *MD : MetadataByGUID[VC.VFunc.GUID]) {
23360b57cec5SDimitry Andric CallSlots[{MD, VC.VFunc.Offset}]
23370b57cec5SDimitry Andric .ConstCSInfo[VC.Args]
23380b57cec5SDimitry Andric .addSummaryTypeCheckedLoadUser(FS);
23390b57cec5SDimitry Andric }
23400b57cec5SDimitry Andric }
23410b57cec5SDimitry Andric }
23420b57cec5SDimitry Andric }
23430b57cec5SDimitry Andric }
23440b57cec5SDimitry Andric
23450b57cec5SDimitry Andric // For each (type, offset) pair:
23460b57cec5SDimitry Andric bool DidVirtualConstProp = false;
234706c3fb27SDimitry Andric std::map<std::string, GlobalValue *> DevirtTargets;
23480b57cec5SDimitry Andric for (auto &S : CallSlots) {
23490b57cec5SDimitry Andric // Search each of the members of the type identifier for the virtual
23500b57cec5SDimitry Andric // function implementation at offset S.first.ByteOffset, and add to
23510b57cec5SDimitry Andric // TargetsForSlot.
23520b57cec5SDimitry Andric std::vector<VirtualCallTarget> TargetsForSlot;
23530b57cec5SDimitry Andric WholeProgramDevirtResolution *Res = nullptr;
23545ffd83dbSDimitry Andric const std::set<TypeMemberInfo> &TypeMemberInfos = TypeIdMap[S.first.TypeID];
23555ffd83dbSDimitry Andric if (ExportSummary && isa<MDString>(S.first.TypeID) &&
23565ffd83dbSDimitry Andric TypeMemberInfos.size())
23575ffd83dbSDimitry Andric // For any type id used on a global's type metadata, create the type id
23585ffd83dbSDimitry Andric // summary resolution regardless of whether we can devirtualize, so that
23595ffd83dbSDimitry Andric // lower type tests knows the type id is not Unsat. If it was not used on
23605ffd83dbSDimitry Andric // a global's type metadata, the TypeIdMap entry set will be empty, and
23615ffd83dbSDimitry Andric // we don't want to create an entry (with the default Unknown type
23625ffd83dbSDimitry Andric // resolution), which can prevent detection of the Unsat.
23630b57cec5SDimitry Andric Res = &ExportSummary
23640b57cec5SDimitry Andric ->getOrInsertTypeIdSummary(
23650b57cec5SDimitry Andric cast<MDString>(S.first.TypeID)->getString())
23660b57cec5SDimitry Andric .WPDRes[S.first.ByteOffset];
23675ffd83dbSDimitry Andric if (tryFindVirtualCallTargets(TargetsForSlot, TypeMemberInfos,
23680eae32dcSDimitry Andric S.first.ByteOffset, ExportSummary)) {
23690b57cec5SDimitry Andric
23708bcb0991SDimitry Andric if (!trySingleImplDevirt(ExportSummary, TargetsForSlot, S.second, Res)) {
23710b57cec5SDimitry Andric DidVirtualConstProp |=
23720b57cec5SDimitry Andric tryVirtualConstProp(TargetsForSlot, S.second, Res, S.first);
23730b57cec5SDimitry Andric
23740b57cec5SDimitry Andric tryICallBranchFunnel(TargetsForSlot, S.second, Res, S.first);
23750b57cec5SDimitry Andric }
23760b57cec5SDimitry Andric
23770b57cec5SDimitry Andric // Collect functions devirtualized at least for one call site for stats.
237881ad6265SDimitry Andric if (RemarksEnabled || AreStatisticsEnabled())
23790b57cec5SDimitry Andric for (const auto &T : TargetsForSlot)
23800b57cec5SDimitry Andric if (T.WasDevirt)
23815ffd83dbSDimitry Andric DevirtTargets[std::string(T.Fn->getName())] = T.Fn;
23820b57cec5SDimitry Andric }
23830b57cec5SDimitry Andric
23840b57cec5SDimitry Andric // CFI-specific: if we are exporting and any llvm.type.checked.load
23850b57cec5SDimitry Andric // intrinsics were *not* devirtualized, we need to add the resulting
23860b57cec5SDimitry Andric // llvm.type.test intrinsics to the function summaries so that the
23870b57cec5SDimitry Andric // LowerTypeTests pass will export them.
23880b57cec5SDimitry Andric if (ExportSummary && isa<MDString>(S.first.TypeID)) {
23890b57cec5SDimitry Andric auto GUID =
23900b57cec5SDimitry Andric GlobalValue::getGUID(cast<MDString>(S.first.TypeID)->getString());
2391bdd1243dSDimitry Andric for (auto *FS : S.second.CSInfo.SummaryTypeCheckedLoadUsers)
23920b57cec5SDimitry Andric FS->addTypeTest(GUID);
23930b57cec5SDimitry Andric for (auto &CCS : S.second.ConstCSInfo)
2394bdd1243dSDimitry Andric for (auto *FS : CCS.second.SummaryTypeCheckedLoadUsers)
23950b57cec5SDimitry Andric FS->addTypeTest(GUID);
23960b57cec5SDimitry Andric }
23970b57cec5SDimitry Andric }
23980b57cec5SDimitry Andric
23990b57cec5SDimitry Andric if (RemarksEnabled) {
24000b57cec5SDimitry Andric // Generate remarks for each devirtualized function.
24010b57cec5SDimitry Andric for (const auto &DT : DevirtTargets) {
240206c3fb27SDimitry Andric GlobalValue *GV = DT.second;
240306c3fb27SDimitry Andric auto F = dyn_cast<Function>(GV);
240406c3fb27SDimitry Andric if (!F) {
240506c3fb27SDimitry Andric auto A = dyn_cast<GlobalAlias>(GV);
240606c3fb27SDimitry Andric assert(A && isa<Function>(A->getAliasee()));
240706c3fb27SDimitry Andric F = dyn_cast<Function>(A->getAliasee());
240806c3fb27SDimitry Andric assert(F);
240906c3fb27SDimitry Andric }
24100b57cec5SDimitry Andric
24110b57cec5SDimitry Andric using namespace ore;
24120b57cec5SDimitry Andric OREGetter(F).emit(OptimizationRemark(DEBUG_TYPE, "Devirtualized", F)
24130b57cec5SDimitry Andric << "devirtualized "
24148bcb0991SDimitry Andric << NV("FunctionName", DT.first));
24150b57cec5SDimitry Andric }
24160b57cec5SDimitry Andric }
24170b57cec5SDimitry Andric
241881ad6265SDimitry Andric NumDevirtTargets += DevirtTargets.size();
241981ad6265SDimitry Andric
24200b57cec5SDimitry Andric removeRedundantTypeTests();
24210b57cec5SDimitry Andric
24220b57cec5SDimitry Andric // Rebuild each global we touched as part of virtual constant propagation to
24230b57cec5SDimitry Andric // include the before and after bytes.
24240b57cec5SDimitry Andric if (DidVirtualConstProp)
24250b57cec5SDimitry Andric for (VTableBits &B : Bits)
24260b57cec5SDimitry Andric rebuildGlobal(B);
24270b57cec5SDimitry Andric
242881ad6265SDimitry Andric // We have lowered or deleted the type intrinsics, so we will no longer have
242981ad6265SDimitry Andric // enough information to reason about the liveness of virtual function
243081ad6265SDimitry Andric // pointers in GlobalDCE.
24318bcb0991SDimitry Andric for (GlobalVariable &GV : M.globals())
24328bcb0991SDimitry Andric GV.eraseMetadata(LLVMContext::MD_vcall_visibility);
24338bcb0991SDimitry Andric
243406c3fb27SDimitry Andric for (auto *CI : CallsWithPtrAuthBundleRemoved)
243506c3fb27SDimitry Andric CI->eraseFromParent();
243606c3fb27SDimitry Andric
24370b57cec5SDimitry Andric return true;
24380b57cec5SDimitry Andric }
24398bcb0991SDimitry Andric
run()24408bcb0991SDimitry Andric void DevirtIndex::run() {
24418bcb0991SDimitry Andric if (ExportSummary.typeIdCompatibleVtableMap().empty())
24428bcb0991SDimitry Andric return;
24438bcb0991SDimitry Andric
24448bcb0991SDimitry Andric DenseMap<GlobalValue::GUID, std::vector<StringRef>> NameByGUID;
2445bdd1243dSDimitry Andric for (const auto &P : ExportSummary.typeIdCompatibleVtableMap()) {
24468bcb0991SDimitry Andric NameByGUID[GlobalValue::getGUID(P.first)].push_back(P.first);
2447bdd1243dSDimitry Andric // Create the type id summary resolution regardlness of whether we can
2448bdd1243dSDimitry Andric // devirtualize, so that lower type tests knows the type id is used on
2449bdd1243dSDimitry Andric // a global and not Unsat. We do this here rather than in the loop over the
2450bdd1243dSDimitry Andric // CallSlots, since that handling will only see type tests that directly
2451bdd1243dSDimitry Andric // feed assumes, and we would miss any that aren't currently handled by WPD
2452bdd1243dSDimitry Andric // (such as type tests that feed assumes via phis).
2453bdd1243dSDimitry Andric ExportSummary.getOrInsertTypeIdSummary(P.first);
24548bcb0991SDimitry Andric }
24558bcb0991SDimitry Andric
24568bcb0991SDimitry Andric // Collect information from summary about which calls to try to devirtualize.
24578bcb0991SDimitry Andric for (auto &P : ExportSummary) {
24588bcb0991SDimitry Andric for (auto &S : P.second.SummaryList) {
24598bcb0991SDimitry Andric auto *FS = dyn_cast<FunctionSummary>(S.get());
24608bcb0991SDimitry Andric if (!FS)
24618bcb0991SDimitry Andric continue;
24628bcb0991SDimitry Andric // FIXME: Only add live functions.
24638bcb0991SDimitry Andric for (FunctionSummary::VFuncId VF : FS->type_test_assume_vcalls()) {
24648bcb0991SDimitry Andric for (StringRef Name : NameByGUID[VF.GUID]) {
24658bcb0991SDimitry Andric CallSlots[{Name, VF.Offset}].CSInfo.addSummaryTypeTestAssumeUser(FS);
24668bcb0991SDimitry Andric }
24678bcb0991SDimitry Andric }
24688bcb0991SDimitry Andric for (FunctionSummary::VFuncId VF : FS->type_checked_load_vcalls()) {
24698bcb0991SDimitry Andric for (StringRef Name : NameByGUID[VF.GUID]) {
24708bcb0991SDimitry Andric CallSlots[{Name, VF.Offset}].CSInfo.addSummaryTypeCheckedLoadUser(FS);
24718bcb0991SDimitry Andric }
24728bcb0991SDimitry Andric }
24738bcb0991SDimitry Andric for (const FunctionSummary::ConstVCall &VC :
24748bcb0991SDimitry Andric FS->type_test_assume_const_vcalls()) {
24758bcb0991SDimitry Andric for (StringRef Name : NameByGUID[VC.VFunc.GUID]) {
24768bcb0991SDimitry Andric CallSlots[{Name, VC.VFunc.Offset}]
24778bcb0991SDimitry Andric .ConstCSInfo[VC.Args]
24788bcb0991SDimitry Andric .addSummaryTypeTestAssumeUser(FS);
24798bcb0991SDimitry Andric }
24808bcb0991SDimitry Andric }
24818bcb0991SDimitry Andric for (const FunctionSummary::ConstVCall &VC :
24828bcb0991SDimitry Andric FS->type_checked_load_const_vcalls()) {
24838bcb0991SDimitry Andric for (StringRef Name : NameByGUID[VC.VFunc.GUID]) {
24848bcb0991SDimitry Andric CallSlots[{Name, VC.VFunc.Offset}]
24858bcb0991SDimitry Andric .ConstCSInfo[VC.Args]
24868bcb0991SDimitry Andric .addSummaryTypeCheckedLoadUser(FS);
24878bcb0991SDimitry Andric }
24888bcb0991SDimitry Andric }
24898bcb0991SDimitry Andric }
24908bcb0991SDimitry Andric }
24918bcb0991SDimitry Andric
24928bcb0991SDimitry Andric std::set<ValueInfo> DevirtTargets;
24938bcb0991SDimitry Andric // For each (type, offset) pair:
24948bcb0991SDimitry Andric for (auto &S : CallSlots) {
24958bcb0991SDimitry Andric // Search each of the members of the type identifier for the virtual
24968bcb0991SDimitry Andric // function implementation at offset S.first.ByteOffset, and add to
24978bcb0991SDimitry Andric // TargetsForSlot.
24988bcb0991SDimitry Andric std::vector<ValueInfo> TargetsForSlot;
24998bcb0991SDimitry Andric auto TidSummary = ExportSummary.getTypeIdCompatibleVtableSummary(S.first.TypeID);
25008bcb0991SDimitry Andric assert(TidSummary);
2501bdd1243dSDimitry Andric // The type id summary would have been created while building the NameByGUID
2502bdd1243dSDimitry Andric // map earlier.
25038bcb0991SDimitry Andric WholeProgramDevirtResolution *Res =
2504bdd1243dSDimitry Andric &ExportSummary.getTypeIdSummary(S.first.TypeID)
2505bdd1243dSDimitry Andric ->WPDRes[S.first.ByteOffset];
25065ffd83dbSDimitry Andric if (tryFindVirtualCallTargets(TargetsForSlot, *TidSummary,
25075ffd83dbSDimitry Andric S.first.ByteOffset)) {
25088bcb0991SDimitry Andric
25098bcb0991SDimitry Andric if (!trySingleImplDevirt(TargetsForSlot, S.first, S.second, Res,
25108bcb0991SDimitry Andric DevirtTargets))
25118bcb0991SDimitry Andric continue;
25128bcb0991SDimitry Andric }
25138bcb0991SDimitry Andric }
25148bcb0991SDimitry Andric
25158bcb0991SDimitry Andric // Optionally have the thin link print message for each devirtualized
25168bcb0991SDimitry Andric // function.
25178bcb0991SDimitry Andric if (PrintSummaryDevirt)
25188bcb0991SDimitry Andric for (const auto &DT : DevirtTargets)
25198bcb0991SDimitry Andric errs() << "Devirtualized call to " << DT << "\n";
252081ad6265SDimitry Andric
252181ad6265SDimitry Andric NumDevirtTargets += DevirtTargets.size();
25228bcb0991SDimitry Andric }
2523