13ca95b02SDimitry Andric //===- WholeProgramDevirt.cpp - Whole program virtual call optimization ---===//
23ca95b02SDimitry Andric //
33ca95b02SDimitry Andric // The LLVM Compiler Infrastructure
43ca95b02SDimitry Andric //
53ca95b02SDimitry Andric // This file is distributed under the University of Illinois Open Source
63ca95b02SDimitry Andric // License. See LICENSE.TXT for details.
73ca95b02SDimitry Andric //
83ca95b02SDimitry Andric //===----------------------------------------------------------------------===//
93ca95b02SDimitry Andric //
103ca95b02SDimitry Andric // This pass implements whole program optimization of virtual calls in cases
113ca95b02SDimitry Andric // where we know (via !type metadata) that the list of callees is fixed. This
123ca95b02SDimitry Andric // includes the following:
133ca95b02SDimitry Andric // - Single implementation devirtualization: if a virtual call has a single
143ca95b02SDimitry Andric // possible callee, replace all calls with a direct call to that callee.
153ca95b02SDimitry Andric // - Virtual constant propagation: if the virtual function's return type is an
163ca95b02SDimitry Andric // integer <=64 bits and all possible callees are readnone, for each class and
173ca95b02SDimitry Andric // each list of constant arguments: evaluate the function, store the return
183ca95b02SDimitry Andric // value alongside the virtual table, and rewrite each virtual call as a load
193ca95b02SDimitry Andric // from the virtual table.
203ca95b02SDimitry Andric // - Uniform return value optimization: if the conditions for virtual constant
213ca95b02SDimitry Andric // propagation hold and each function returns the same constant value, replace
223ca95b02SDimitry Andric // each virtual call with that constant.
233ca95b02SDimitry Andric // - Unique return value optimization for i1 return values: if the conditions
243ca95b02SDimitry Andric // for virtual constant propagation hold and a single vtable's function
253ca95b02SDimitry Andric // returns 0, or a single vtable's function returns 1, replace each virtual
263ca95b02SDimitry Andric // call with a comparison of the vptr against that vtable's address.
273ca95b02SDimitry Andric //
287a7e6055SDimitry Andric // This pass is intended to be used during the regular and thin LTO pipelines.
297a7e6055SDimitry Andric // During regular LTO, the pass determines the best optimization for each
307a7e6055SDimitry Andric // virtual call and applies the resolutions directly to virtual calls that are
317a7e6055SDimitry Andric // eligible for virtual call optimization (i.e. calls that use either of the
327a7e6055SDimitry Andric // llvm.assume(llvm.type.test) or llvm.type.checked.load intrinsics). During
337a7e6055SDimitry Andric // ThinLTO, the pass operates in two phases:
347a7e6055SDimitry Andric // - Export phase: this is run during the thin link over a single merged module
357a7e6055SDimitry Andric // that contains all vtables with !type metadata that participate in the link.
367a7e6055SDimitry Andric // The pass computes a resolution for each virtual call and stores it in the
377a7e6055SDimitry Andric // type identifier summary.
387a7e6055SDimitry Andric // - Import phase: this is run during the thin backends over the individual
397a7e6055SDimitry Andric // modules. The pass applies the resolutions previously computed during the
407a7e6055SDimitry Andric // import phase to each eligible virtual call.
417a7e6055SDimitry Andric //
423ca95b02SDimitry Andric //===----------------------------------------------------------------------===//
433ca95b02SDimitry Andric
443ca95b02SDimitry Andric #include "llvm/Transforms/IPO/WholeProgramDevirt.h"
453ca95b02SDimitry Andric #include "llvm/ADT/ArrayRef.h"
46d88c1a5aSDimitry Andric #include "llvm/ADT/DenseMap.h"
47d88c1a5aSDimitry Andric #include "llvm/ADT/DenseMapInfo.h"
483ca95b02SDimitry Andric #include "llvm/ADT/DenseSet.h"
493ca95b02SDimitry Andric #include "llvm/ADT/MapVector.h"
50d88c1a5aSDimitry Andric #include "llvm/ADT/SmallVector.h"
51db17bf38SDimitry Andric #include "llvm/ADT/iterator_range.h"
527a7e6055SDimitry Andric #include "llvm/Analysis/AliasAnalysis.h"
537a7e6055SDimitry Andric #include "llvm/Analysis/BasicAliasAnalysis.h"
542cab237bSDimitry Andric #include "llvm/Analysis/OptimizationRemarkEmitter.h"
553ca95b02SDimitry Andric #include "llvm/Analysis/TypeMetadataUtils.h"
563ca95b02SDimitry Andric #include "llvm/IR/CallSite.h"
573ca95b02SDimitry Andric #include "llvm/IR/Constants.h"
583ca95b02SDimitry Andric #include "llvm/IR/DataLayout.h"
59d88c1a5aSDimitry Andric #include "llvm/IR/DebugLoc.h"
60d88c1a5aSDimitry Andric #include "llvm/IR/DerivedTypes.h"
61*b5893f02SDimitry Andric #include "llvm/IR/Dominators.h"
62d88c1a5aSDimitry Andric #include "llvm/IR/Function.h"
63d88c1a5aSDimitry Andric #include "llvm/IR/GlobalAlias.h"
64d88c1a5aSDimitry Andric #include "llvm/IR/GlobalVariable.h"
653ca95b02SDimitry Andric #include "llvm/IR/IRBuilder.h"
66d88c1a5aSDimitry Andric #include "llvm/IR/InstrTypes.h"
67d88c1a5aSDimitry Andric #include "llvm/IR/Instruction.h"
683ca95b02SDimitry Andric #include "llvm/IR/Instructions.h"
693ca95b02SDimitry Andric #include "llvm/IR/Intrinsics.h"
70d88c1a5aSDimitry Andric #include "llvm/IR/LLVMContext.h"
71d88c1a5aSDimitry Andric #include "llvm/IR/Metadata.h"
723ca95b02SDimitry Andric #include "llvm/IR/Module.h"
737a7e6055SDimitry Andric #include "llvm/IR/ModuleSummaryIndexYAML.h"
743ca95b02SDimitry Andric #include "llvm/Pass.h"
75d88c1a5aSDimitry Andric #include "llvm/PassRegistry.h"
76d88c1a5aSDimitry Andric #include "llvm/PassSupport.h"
77d88c1a5aSDimitry Andric #include "llvm/Support/Casting.h"
787a7e6055SDimitry Andric #include "llvm/Support/Error.h"
797a7e6055SDimitry Andric #include "llvm/Support/FileSystem.h"
80d88c1a5aSDimitry Andric #include "llvm/Support/MathExtras.h"
813ca95b02SDimitry Andric #include "llvm/Transforms/IPO.h"
827a7e6055SDimitry Andric #include "llvm/Transforms/IPO/FunctionAttrs.h"
833ca95b02SDimitry Andric #include "llvm/Transforms/Utils/Evaluator.h"
84d88c1a5aSDimitry Andric #include <algorithm>
85d88c1a5aSDimitry Andric #include <cstddef>
86d88c1a5aSDimitry Andric #include <map>
873ca95b02SDimitry Andric #include <set>
88d88c1a5aSDimitry Andric #include <string>
893ca95b02SDimitry Andric
903ca95b02SDimitry Andric using namespace llvm;
913ca95b02SDimitry Andric using namespace wholeprogramdevirt;
923ca95b02SDimitry Andric
933ca95b02SDimitry Andric #define DEBUG_TYPE "wholeprogramdevirt"
943ca95b02SDimitry Andric
957a7e6055SDimitry Andric static cl::opt<PassSummaryAction> ClSummaryAction(
967a7e6055SDimitry Andric "wholeprogramdevirt-summary-action",
977a7e6055SDimitry Andric cl::desc("What to do with the summary when running this pass"),
987a7e6055SDimitry Andric cl::values(clEnumValN(PassSummaryAction::None, "none", "Do nothing"),
997a7e6055SDimitry Andric clEnumValN(PassSummaryAction::Import, "import",
1007a7e6055SDimitry Andric "Import typeid resolutions from summary and globals"),
1017a7e6055SDimitry Andric clEnumValN(PassSummaryAction::Export, "export",
1027a7e6055SDimitry Andric "Export typeid resolutions to summary and globals")),
1037a7e6055SDimitry Andric cl::Hidden);
1047a7e6055SDimitry Andric
1057a7e6055SDimitry Andric static cl::opt<std::string> ClReadSummary(
1067a7e6055SDimitry Andric "wholeprogramdevirt-read-summary",
1077a7e6055SDimitry Andric cl::desc("Read summary from given YAML file before running pass"),
1087a7e6055SDimitry Andric cl::Hidden);
1097a7e6055SDimitry Andric
1107a7e6055SDimitry Andric static cl::opt<std::string> ClWriteSummary(
1117a7e6055SDimitry Andric "wholeprogramdevirt-write-summary",
1127a7e6055SDimitry Andric cl::desc("Write summary to given YAML file after running pass"),
1137a7e6055SDimitry Andric cl::Hidden);
1147a7e6055SDimitry Andric
1154ba319b5SDimitry Andric static cl::opt<unsigned>
1164ba319b5SDimitry Andric ClThreshold("wholeprogramdevirt-branch-funnel-threshold", cl::Hidden,
1174ba319b5SDimitry Andric cl::init(10), cl::ZeroOrMore,
1184ba319b5SDimitry Andric cl::desc("Maximum number of call targets per "
1194ba319b5SDimitry Andric "call site to enable branch funnels"));
1204ba319b5SDimitry Andric
1213ca95b02SDimitry Andric // Find the minimum offset that we may store a value of size Size bits at. If
1223ca95b02SDimitry Andric // IsAfter is set, look for an offset before the object, otherwise look for an
1233ca95b02SDimitry Andric // offset after the object.
1243ca95b02SDimitry Andric uint64_t
findLowestOffset(ArrayRef<VirtualCallTarget> Targets,bool IsAfter,uint64_t Size)1253ca95b02SDimitry Andric wholeprogramdevirt::findLowestOffset(ArrayRef<VirtualCallTarget> Targets,
1263ca95b02SDimitry Andric bool IsAfter, uint64_t Size) {
1273ca95b02SDimitry Andric // Find a minimum offset taking into account only vtable sizes.
1283ca95b02SDimitry Andric uint64_t MinByte = 0;
1293ca95b02SDimitry Andric for (const VirtualCallTarget &Target : Targets) {
1303ca95b02SDimitry Andric if (IsAfter)
1313ca95b02SDimitry Andric MinByte = std::max(MinByte, Target.minAfterBytes());
1323ca95b02SDimitry Andric else
1333ca95b02SDimitry Andric MinByte = std::max(MinByte, Target.minBeforeBytes());
1343ca95b02SDimitry Andric }
1353ca95b02SDimitry Andric
1363ca95b02SDimitry Andric // Build a vector of arrays of bytes covering, for each target, a slice of the
1373ca95b02SDimitry Andric // used region (see AccumBitVector::BytesUsed in
1383ca95b02SDimitry Andric // llvm/Transforms/IPO/WholeProgramDevirt.h) starting at MinByte. Effectively,
1393ca95b02SDimitry Andric // this aligns the used regions to start at MinByte.
1403ca95b02SDimitry Andric //
1413ca95b02SDimitry Andric // In this example, A, B and C are vtables, # is a byte already allocated for
1423ca95b02SDimitry Andric // a virtual function pointer, AAAA... (etc.) are the used regions for the
1433ca95b02SDimitry Andric // vtables and Offset(X) is the value computed for the Offset variable below
1443ca95b02SDimitry Andric // for X.
1453ca95b02SDimitry Andric //
1463ca95b02SDimitry Andric // Offset(A)
1473ca95b02SDimitry Andric // | |
1483ca95b02SDimitry Andric // |MinByte
1493ca95b02SDimitry Andric // A: ################AAAAAAAA|AAAAAAAA
1503ca95b02SDimitry Andric // B: ########BBBBBBBBBBBBBBBB|BBBB
1513ca95b02SDimitry Andric // C: ########################|CCCCCCCCCCCCCCCC
1523ca95b02SDimitry Andric // | Offset(B) |
1533ca95b02SDimitry Andric //
1543ca95b02SDimitry Andric // This code produces the slices of A, B and C that appear after the divider
1553ca95b02SDimitry Andric // at MinByte.
1563ca95b02SDimitry Andric std::vector<ArrayRef<uint8_t>> Used;
1573ca95b02SDimitry Andric for (const VirtualCallTarget &Target : Targets) {
1583ca95b02SDimitry Andric ArrayRef<uint8_t> VTUsed = IsAfter ? Target.TM->Bits->After.BytesUsed
1593ca95b02SDimitry Andric : Target.TM->Bits->Before.BytesUsed;
1603ca95b02SDimitry Andric uint64_t Offset = IsAfter ? MinByte - Target.minAfterBytes()
1613ca95b02SDimitry Andric : MinByte - Target.minBeforeBytes();
1623ca95b02SDimitry Andric
1633ca95b02SDimitry Andric // Disregard used regions that are smaller than Offset. These are
1643ca95b02SDimitry Andric // effectively all-free regions that do not need to be checked.
1653ca95b02SDimitry Andric if (VTUsed.size() > Offset)
1663ca95b02SDimitry Andric Used.push_back(VTUsed.slice(Offset));
1673ca95b02SDimitry Andric }
1683ca95b02SDimitry Andric
1693ca95b02SDimitry Andric if (Size == 1) {
1703ca95b02SDimitry Andric // Find a free bit in each member of Used.
1713ca95b02SDimitry Andric for (unsigned I = 0;; ++I) {
1723ca95b02SDimitry Andric uint8_t BitsUsed = 0;
1733ca95b02SDimitry Andric for (auto &&B : Used)
1743ca95b02SDimitry Andric if (I < B.size())
1753ca95b02SDimitry Andric BitsUsed |= B[I];
1763ca95b02SDimitry Andric if (BitsUsed != 0xff)
1773ca95b02SDimitry Andric return (MinByte + I) * 8 +
1783ca95b02SDimitry Andric countTrailingZeros(uint8_t(~BitsUsed), ZB_Undefined);
1793ca95b02SDimitry Andric }
1803ca95b02SDimitry Andric } else {
1813ca95b02SDimitry Andric // Find a free (Size/8) byte region in each member of Used.
1823ca95b02SDimitry Andric // FIXME: see if alignment helps.
1833ca95b02SDimitry Andric for (unsigned I = 0;; ++I) {
1843ca95b02SDimitry Andric for (auto &&B : Used) {
1853ca95b02SDimitry Andric unsigned Byte = 0;
1863ca95b02SDimitry Andric while ((I + Byte) < B.size() && Byte < (Size / 8)) {
1873ca95b02SDimitry Andric if (B[I + Byte])
1883ca95b02SDimitry Andric goto NextI;
1893ca95b02SDimitry Andric ++Byte;
1903ca95b02SDimitry Andric }
1913ca95b02SDimitry Andric }
1923ca95b02SDimitry Andric return (MinByte + I) * 8;
1933ca95b02SDimitry Andric NextI:;
1943ca95b02SDimitry Andric }
1953ca95b02SDimitry Andric }
1963ca95b02SDimitry Andric }
1973ca95b02SDimitry Andric
setBeforeReturnValues(MutableArrayRef<VirtualCallTarget> Targets,uint64_t AllocBefore,unsigned BitWidth,int64_t & OffsetByte,uint64_t & OffsetBit)1983ca95b02SDimitry Andric void wholeprogramdevirt::setBeforeReturnValues(
1993ca95b02SDimitry Andric MutableArrayRef<VirtualCallTarget> Targets, uint64_t AllocBefore,
2003ca95b02SDimitry Andric unsigned BitWidth, int64_t &OffsetByte, uint64_t &OffsetBit) {
2013ca95b02SDimitry Andric if (BitWidth == 1)
2023ca95b02SDimitry Andric OffsetByte = -(AllocBefore / 8 + 1);
2033ca95b02SDimitry Andric else
2043ca95b02SDimitry Andric OffsetByte = -((AllocBefore + 7) / 8 + (BitWidth + 7) / 8);
2053ca95b02SDimitry Andric OffsetBit = AllocBefore % 8;
2063ca95b02SDimitry Andric
2073ca95b02SDimitry Andric for (VirtualCallTarget &Target : Targets) {
2083ca95b02SDimitry Andric if (BitWidth == 1)
2093ca95b02SDimitry Andric Target.setBeforeBit(AllocBefore);
2103ca95b02SDimitry Andric else
2113ca95b02SDimitry Andric Target.setBeforeBytes(AllocBefore, (BitWidth + 7) / 8);
2123ca95b02SDimitry Andric }
2133ca95b02SDimitry Andric }
2143ca95b02SDimitry Andric
setAfterReturnValues(MutableArrayRef<VirtualCallTarget> Targets,uint64_t AllocAfter,unsigned BitWidth,int64_t & OffsetByte,uint64_t & OffsetBit)2153ca95b02SDimitry Andric void wholeprogramdevirt::setAfterReturnValues(
2163ca95b02SDimitry Andric MutableArrayRef<VirtualCallTarget> Targets, uint64_t AllocAfter,
2173ca95b02SDimitry Andric unsigned BitWidth, int64_t &OffsetByte, uint64_t &OffsetBit) {
2183ca95b02SDimitry Andric if (BitWidth == 1)
2193ca95b02SDimitry Andric OffsetByte = AllocAfter / 8;
2203ca95b02SDimitry Andric else
2213ca95b02SDimitry Andric OffsetByte = (AllocAfter + 7) / 8;
2223ca95b02SDimitry Andric OffsetBit = AllocAfter % 8;
2233ca95b02SDimitry Andric
2243ca95b02SDimitry Andric for (VirtualCallTarget &Target : Targets) {
2253ca95b02SDimitry Andric if (BitWidth == 1)
2263ca95b02SDimitry Andric Target.setAfterBit(AllocAfter);
2273ca95b02SDimitry Andric else
2283ca95b02SDimitry Andric Target.setAfterBytes(AllocAfter, (BitWidth + 7) / 8);
2293ca95b02SDimitry Andric }
2303ca95b02SDimitry Andric }
2313ca95b02SDimitry Andric
VirtualCallTarget(Function * Fn,const TypeMemberInfo * TM)2323ca95b02SDimitry Andric VirtualCallTarget::VirtualCallTarget(Function *Fn, const TypeMemberInfo *TM)
2333ca95b02SDimitry Andric : Fn(Fn), TM(TM),
234d88c1a5aSDimitry Andric IsBigEndian(Fn->getParent()->getDataLayout().isBigEndian()), WasDevirt(false) {}
2353ca95b02SDimitry Andric
2363ca95b02SDimitry Andric namespace {
2373ca95b02SDimitry Andric
2383ca95b02SDimitry Andric // A slot in a set of virtual tables. The TypeID identifies the set of virtual
2393ca95b02SDimitry Andric // tables, and the ByteOffset is the offset in bytes from the address point to
2403ca95b02SDimitry Andric // the virtual function pointer.
2413ca95b02SDimitry Andric struct VTableSlot {
2423ca95b02SDimitry Andric Metadata *TypeID;
2433ca95b02SDimitry Andric uint64_t ByteOffset;
2443ca95b02SDimitry Andric };
2453ca95b02SDimitry Andric
246d88c1a5aSDimitry Andric } // end anonymous namespace
2473ca95b02SDimitry Andric
2483ca95b02SDimitry Andric namespace llvm {
2493ca95b02SDimitry Andric
2503ca95b02SDimitry Andric template <> struct DenseMapInfo<VTableSlot> {
getEmptyKeyllvm::DenseMapInfo2513ca95b02SDimitry Andric static VTableSlot getEmptyKey() {
2523ca95b02SDimitry Andric return {DenseMapInfo<Metadata *>::getEmptyKey(),
2533ca95b02SDimitry Andric DenseMapInfo<uint64_t>::getEmptyKey()};
2543ca95b02SDimitry Andric }
getTombstoneKeyllvm::DenseMapInfo2553ca95b02SDimitry Andric static VTableSlot getTombstoneKey() {
2563ca95b02SDimitry Andric return {DenseMapInfo<Metadata *>::getTombstoneKey(),
2573ca95b02SDimitry Andric DenseMapInfo<uint64_t>::getTombstoneKey()};
2583ca95b02SDimitry Andric }
getHashValuellvm::DenseMapInfo2593ca95b02SDimitry Andric static unsigned getHashValue(const VTableSlot &I) {
2603ca95b02SDimitry Andric return DenseMapInfo<Metadata *>::getHashValue(I.TypeID) ^
2613ca95b02SDimitry Andric DenseMapInfo<uint64_t>::getHashValue(I.ByteOffset);
2623ca95b02SDimitry Andric }
isEqualllvm::DenseMapInfo2633ca95b02SDimitry Andric static bool isEqual(const VTableSlot &LHS,
2643ca95b02SDimitry Andric const VTableSlot &RHS) {
2653ca95b02SDimitry Andric return LHS.TypeID == RHS.TypeID && LHS.ByteOffset == RHS.ByteOffset;
2663ca95b02SDimitry Andric }
2673ca95b02SDimitry Andric };
2683ca95b02SDimitry Andric
269d88c1a5aSDimitry Andric } // end namespace llvm
2703ca95b02SDimitry Andric
2713ca95b02SDimitry Andric namespace {
2723ca95b02SDimitry Andric
2733ca95b02SDimitry Andric // A virtual call site. VTable is the loaded virtual table pointer, and CS is
2743ca95b02SDimitry Andric // the indirect virtual call.
2753ca95b02SDimitry Andric struct VirtualCallSite {
2763ca95b02SDimitry Andric Value *VTable;
2773ca95b02SDimitry Andric CallSite CS;
2783ca95b02SDimitry Andric
2793ca95b02SDimitry Andric // If non-null, this field points to the associated unsafe use count stored in
2803ca95b02SDimitry Andric // the DevirtModule::NumUnsafeUsesForTypeTest map below. See the description
2813ca95b02SDimitry Andric // of that field for details.
2823ca95b02SDimitry Andric unsigned *NumUnsafeUses;
2833ca95b02SDimitry Andric
2842cab237bSDimitry Andric void
emitRemark__anon3453c20f0211::VirtualCallSite2852cab237bSDimitry Andric emitRemark(const StringRef OptName, const StringRef TargetName,
2862cab237bSDimitry Andric function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter) {
2873ca95b02SDimitry Andric Function *F = CS.getCaller();
2882cab237bSDimitry Andric DebugLoc DLoc = CS->getDebugLoc();
2892cab237bSDimitry Andric BasicBlock *Block = CS.getParent();
2902cab237bSDimitry Andric
2912cab237bSDimitry Andric using namespace ore;
2924ba319b5SDimitry Andric OREGetter(F).emit(OptimizationRemark(DEBUG_TYPE, OptName, DLoc, Block)
2934ba319b5SDimitry Andric << NV("Optimization", OptName)
2944ba319b5SDimitry Andric << ": devirtualized a call to "
2952cab237bSDimitry Andric << NV("FunctionName", TargetName));
2962cab237bSDimitry Andric }
2972cab237bSDimitry Andric
replaceAndErase__anon3453c20f0211::VirtualCallSite2982cab237bSDimitry Andric void replaceAndErase(
2992cab237bSDimitry Andric const StringRef OptName, const StringRef TargetName, bool RemarksEnabled,
3002cab237bSDimitry Andric function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter,
3012cab237bSDimitry Andric Value *New) {
302d88c1a5aSDimitry Andric if (RemarksEnabled)
3032cab237bSDimitry Andric emitRemark(OptName, TargetName, OREGetter);
3043ca95b02SDimitry Andric CS->replaceAllUsesWith(New);
3053ca95b02SDimitry Andric if (auto II = dyn_cast<InvokeInst>(CS.getInstruction())) {
3063ca95b02SDimitry Andric BranchInst::Create(II->getNormalDest(), CS.getInstruction());
3073ca95b02SDimitry Andric II->getUnwindDest()->removePredecessor(II->getParent());
3083ca95b02SDimitry Andric }
3093ca95b02SDimitry Andric CS->eraseFromParent();
3103ca95b02SDimitry Andric // This use is no longer unsafe.
3113ca95b02SDimitry Andric if (NumUnsafeUses)
3123ca95b02SDimitry Andric --*NumUnsafeUses;
3133ca95b02SDimitry Andric }
3143ca95b02SDimitry Andric };
3153ca95b02SDimitry Andric
3167a7e6055SDimitry Andric // Call site information collected for a specific VTableSlot and possibly a list
3177a7e6055SDimitry Andric // of constant integer arguments. The grouping by arguments is handled by the
3187a7e6055SDimitry Andric // VTableSlotInfo class.
3197a7e6055SDimitry Andric struct CallSiteInfo {
3207a7e6055SDimitry Andric /// The set of call sites for this slot. Used during regular LTO and the
3217a7e6055SDimitry Andric /// import phase of ThinLTO (as well as the export phase of ThinLTO for any
3227a7e6055SDimitry Andric /// call sites that appear in the merged module itself); in each of these
3237a7e6055SDimitry Andric /// cases we are directly operating on the call sites at the IR level.
3247a7e6055SDimitry Andric std::vector<VirtualCallSite> CallSites;
3257a7e6055SDimitry Andric
3264ba319b5SDimitry Andric /// Whether all call sites represented by this CallSiteInfo, including those
3274ba319b5SDimitry Andric /// in summaries, have been devirtualized. This starts off as true because a
3284ba319b5SDimitry Andric /// default constructed CallSiteInfo represents no call sites.
3294ba319b5SDimitry Andric bool AllCallSitesDevirted = true;
3304ba319b5SDimitry Andric
3317a7e6055SDimitry Andric // These fields are used during the export phase of ThinLTO and reflect
3327a7e6055SDimitry Andric // information collected from function summaries.
3337a7e6055SDimitry Andric
3347a7e6055SDimitry Andric /// Whether any function summary contains an llvm.assume(llvm.type.test) for
3357a7e6055SDimitry Andric /// this slot.
3364ba319b5SDimitry Andric bool SummaryHasTypeTestAssumeUsers = false;
3377a7e6055SDimitry Andric
3387a7e6055SDimitry Andric /// CFI-specific: a vector containing the list of function summaries that use
3397a7e6055SDimitry Andric /// the llvm.type.checked.load intrinsic and therefore will require
3407a7e6055SDimitry Andric /// resolutions for llvm.type.test in order to implement CFI checks if
3417a7e6055SDimitry Andric /// devirtualization was unsuccessful. If devirtualization was successful, the
3427a7e6055SDimitry Andric /// pass will clear this vector by calling markDevirt(). If at the end of the
3437a7e6055SDimitry Andric /// pass the vector is non-empty, we will need to add a use of llvm.type.test
3447a7e6055SDimitry Andric /// to each of the function summaries in the vector.
3457a7e6055SDimitry Andric std::vector<FunctionSummary *> SummaryTypeCheckedLoadUsers;
3467a7e6055SDimitry Andric
isExported__anon3453c20f0211::CallSiteInfo3477a7e6055SDimitry Andric bool isExported() const {
3487a7e6055SDimitry Andric return SummaryHasTypeTestAssumeUsers ||
3497a7e6055SDimitry Andric !SummaryTypeCheckedLoadUsers.empty();
3507a7e6055SDimitry Andric }
3517a7e6055SDimitry Andric
markSummaryHasTypeTestAssumeUsers__anon3453c20f0211::CallSiteInfo3524ba319b5SDimitry Andric void markSummaryHasTypeTestAssumeUsers() {
3534ba319b5SDimitry Andric SummaryHasTypeTestAssumeUsers = true;
3544ba319b5SDimitry Andric AllCallSitesDevirted = false;
3554ba319b5SDimitry Andric }
3564ba319b5SDimitry Andric
addSummaryTypeCheckedLoadUser__anon3453c20f0211::CallSiteInfo3574ba319b5SDimitry Andric void addSummaryTypeCheckedLoadUser(FunctionSummary *FS) {
3584ba319b5SDimitry Andric SummaryTypeCheckedLoadUsers.push_back(FS);
3594ba319b5SDimitry Andric AllCallSitesDevirted = false;
3604ba319b5SDimitry Andric }
3614ba319b5SDimitry Andric
markDevirt__anon3453c20f0211::CallSiteInfo3624ba319b5SDimitry Andric void markDevirt() {
3634ba319b5SDimitry Andric AllCallSitesDevirted = true;
3644ba319b5SDimitry Andric
3654ba319b5SDimitry Andric // As explained in the comment for SummaryTypeCheckedLoadUsers.
3664ba319b5SDimitry Andric SummaryTypeCheckedLoadUsers.clear();
3674ba319b5SDimitry Andric }
3687a7e6055SDimitry Andric };
3697a7e6055SDimitry Andric
3707a7e6055SDimitry Andric // Call site information collected for a specific VTableSlot.
3717a7e6055SDimitry Andric struct VTableSlotInfo {
3727a7e6055SDimitry Andric // The set of call sites which do not have all constant integer arguments
3737a7e6055SDimitry Andric // (excluding "this").
3747a7e6055SDimitry Andric CallSiteInfo CSInfo;
3757a7e6055SDimitry Andric
3767a7e6055SDimitry Andric // The set of call sites with all constant integer arguments (excluding
3777a7e6055SDimitry Andric // "this"), grouped by argument list.
3787a7e6055SDimitry Andric std::map<std::vector<uint64_t>, CallSiteInfo> ConstCSInfo;
3797a7e6055SDimitry Andric
3807a7e6055SDimitry Andric void addCallSite(Value *VTable, CallSite CS, unsigned *NumUnsafeUses);
3817a7e6055SDimitry Andric
3827a7e6055SDimitry Andric private:
3837a7e6055SDimitry Andric CallSiteInfo &findCallSiteInfo(CallSite CS);
3847a7e6055SDimitry Andric };
3857a7e6055SDimitry Andric
findCallSiteInfo(CallSite CS)3867a7e6055SDimitry Andric CallSiteInfo &VTableSlotInfo::findCallSiteInfo(CallSite CS) {
3877a7e6055SDimitry Andric std::vector<uint64_t> Args;
3887a7e6055SDimitry Andric auto *CI = dyn_cast<IntegerType>(CS.getType());
3897a7e6055SDimitry Andric if (!CI || CI->getBitWidth() > 64 || CS.arg_empty())
3907a7e6055SDimitry Andric return CSInfo;
3917a7e6055SDimitry Andric for (auto &&Arg : make_range(CS.arg_begin() + 1, CS.arg_end())) {
3927a7e6055SDimitry Andric auto *CI = dyn_cast<ConstantInt>(Arg);
3937a7e6055SDimitry Andric if (!CI || CI->getBitWidth() > 64)
3947a7e6055SDimitry Andric return CSInfo;
3957a7e6055SDimitry Andric Args.push_back(CI->getZExtValue());
3967a7e6055SDimitry Andric }
3977a7e6055SDimitry Andric return ConstCSInfo[Args];
3987a7e6055SDimitry Andric }
3997a7e6055SDimitry Andric
addCallSite(Value * VTable,CallSite CS,unsigned * NumUnsafeUses)4007a7e6055SDimitry Andric void VTableSlotInfo::addCallSite(Value *VTable, CallSite CS,
4017a7e6055SDimitry Andric unsigned *NumUnsafeUses) {
4024ba319b5SDimitry Andric auto &CSI = findCallSiteInfo(CS);
4034ba319b5SDimitry Andric CSI.AllCallSitesDevirted = false;
4044ba319b5SDimitry Andric CSI.CallSites.push_back({VTable, CS, NumUnsafeUses});
4057a7e6055SDimitry Andric }
4067a7e6055SDimitry Andric
4073ca95b02SDimitry Andric struct DevirtModule {
4083ca95b02SDimitry Andric Module &M;
4097a7e6055SDimitry Andric function_ref<AAResults &(Function &)> AARGetter;
410*b5893f02SDimitry Andric function_ref<DominatorTree &(Function &)> LookupDomTree;
4117a7e6055SDimitry Andric
4127a7e6055SDimitry Andric ModuleSummaryIndex *ExportSummary;
4137a7e6055SDimitry Andric const ModuleSummaryIndex *ImportSummary;
4147a7e6055SDimitry Andric
4153ca95b02SDimitry Andric IntegerType *Int8Ty;
4163ca95b02SDimitry Andric PointerType *Int8PtrTy;
4173ca95b02SDimitry Andric IntegerType *Int32Ty;
4187a7e6055SDimitry Andric IntegerType *Int64Ty;
4197a7e6055SDimitry Andric IntegerType *IntPtrTy;
4203ca95b02SDimitry Andric
421d88c1a5aSDimitry Andric bool RemarksEnabled;
4222cab237bSDimitry Andric function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter;
423d88c1a5aSDimitry Andric
4247a7e6055SDimitry Andric MapVector<VTableSlot, VTableSlotInfo> CallSlots;
4253ca95b02SDimitry Andric
4263ca95b02SDimitry Andric // This map keeps track of the number of "unsafe" uses of a loaded function
4273ca95b02SDimitry Andric // pointer. The key is the associated llvm.type.test intrinsic call generated
4283ca95b02SDimitry Andric // by this pass. An unsafe use is one that calls the loaded function pointer
4293ca95b02SDimitry Andric // directly. Every time we eliminate an unsafe use (for example, by
4303ca95b02SDimitry Andric // devirtualizing it or by applying virtual constant propagation), we
4313ca95b02SDimitry Andric // decrement the value stored in this map. If a value reaches zero, we can
4323ca95b02SDimitry Andric // eliminate the type check by RAUWing the associated llvm.type.test call with
4333ca95b02SDimitry Andric // true.
4343ca95b02SDimitry Andric std::map<CallInst *, unsigned> NumUnsafeUsesForTypeTest;
4353ca95b02SDimitry Andric
DevirtModule__anon3453c20f0211::DevirtModule4367a7e6055SDimitry Andric DevirtModule(Module &M, function_ref<AAResults &(Function &)> AARGetter,
4372cab237bSDimitry Andric function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter,
438*b5893f02SDimitry Andric function_ref<DominatorTree &(Function &)> LookupDomTree,
4397a7e6055SDimitry Andric ModuleSummaryIndex *ExportSummary,
4407a7e6055SDimitry Andric const ModuleSummaryIndex *ImportSummary)
441*b5893f02SDimitry Andric : M(M), AARGetter(AARGetter), LookupDomTree(LookupDomTree),
442*b5893f02SDimitry Andric ExportSummary(ExportSummary), ImportSummary(ImportSummary),
443*b5893f02SDimitry Andric Int8Ty(Type::getInt8Ty(M.getContext())),
4443ca95b02SDimitry Andric Int8PtrTy(Type::getInt8PtrTy(M.getContext())),
445d88c1a5aSDimitry Andric Int32Ty(Type::getInt32Ty(M.getContext())),
4467a7e6055SDimitry Andric Int64Ty(Type::getInt64Ty(M.getContext())),
4477a7e6055SDimitry Andric IntPtrTy(M.getDataLayout().getIntPtrType(M.getContext(), 0)),
4482cab237bSDimitry Andric RemarksEnabled(areRemarksEnabled()), OREGetter(OREGetter) {
4497a7e6055SDimitry Andric assert(!(ExportSummary && ImportSummary));
4507a7e6055SDimitry Andric }
451d88c1a5aSDimitry Andric
452d88c1a5aSDimitry Andric bool areRemarksEnabled();
4533ca95b02SDimitry Andric
4543ca95b02SDimitry Andric void scanTypeTestUsers(Function *TypeTestFunc, Function *AssumeFunc);
4553ca95b02SDimitry Andric void scanTypeCheckedLoadUsers(Function *TypeCheckedLoadFunc);
4563ca95b02SDimitry Andric
4573ca95b02SDimitry Andric void buildTypeIdentifierMap(
4583ca95b02SDimitry Andric std::vector<VTableBits> &Bits,
4593ca95b02SDimitry Andric DenseMap<Metadata *, std::set<TypeMemberInfo>> &TypeIdMap);
460d88c1a5aSDimitry Andric Constant *getPointerAtOffset(Constant *I, uint64_t Offset);
4613ca95b02SDimitry Andric bool
4623ca95b02SDimitry Andric tryFindVirtualCallTargets(std::vector<VirtualCallTarget> &TargetsForSlot,
4633ca95b02SDimitry Andric const std::set<TypeMemberInfo> &TypeMemberInfos,
4643ca95b02SDimitry Andric uint64_t ByteOffset);
4657a7e6055SDimitry Andric
4667a7e6055SDimitry Andric void applySingleImplDevirt(VTableSlotInfo &SlotInfo, Constant *TheFn,
4677a7e6055SDimitry Andric bool &IsExported);
468d88c1a5aSDimitry Andric bool trySingleImplDevirt(MutableArrayRef<VirtualCallTarget> TargetsForSlot,
4697a7e6055SDimitry Andric VTableSlotInfo &SlotInfo,
4707a7e6055SDimitry Andric WholeProgramDevirtResolution *Res);
4717a7e6055SDimitry Andric
4724ba319b5SDimitry Andric void applyICallBranchFunnel(VTableSlotInfo &SlotInfo, Constant *JT,
4734ba319b5SDimitry Andric bool &IsExported);
4744ba319b5SDimitry Andric void tryICallBranchFunnel(MutableArrayRef<VirtualCallTarget> TargetsForSlot,
4754ba319b5SDimitry Andric VTableSlotInfo &SlotInfo,
4764ba319b5SDimitry Andric WholeProgramDevirtResolution *Res, VTableSlot Slot);
4774ba319b5SDimitry Andric
4783ca95b02SDimitry Andric bool tryEvaluateFunctionsWithArgs(
4793ca95b02SDimitry Andric MutableArrayRef<VirtualCallTarget> TargetsForSlot,
4807a7e6055SDimitry Andric ArrayRef<uint64_t> Args);
4817a7e6055SDimitry Andric
4827a7e6055SDimitry Andric void applyUniformRetValOpt(CallSiteInfo &CSInfo, StringRef FnName,
4837a7e6055SDimitry Andric uint64_t TheRetVal);
4847a7e6055SDimitry Andric bool tryUniformRetValOpt(MutableArrayRef<VirtualCallTarget> TargetsForSlot,
4857a7e6055SDimitry Andric CallSiteInfo &CSInfo,
4867a7e6055SDimitry Andric WholeProgramDevirtResolution::ByArg *Res);
4877a7e6055SDimitry Andric
4887a7e6055SDimitry Andric // Returns the global symbol name that is used to export information about the
4897a7e6055SDimitry Andric // given vtable slot and list of arguments.
4907a7e6055SDimitry Andric std::string getGlobalName(VTableSlot Slot, ArrayRef<uint64_t> Args,
4917a7e6055SDimitry Andric StringRef Name);
4927a7e6055SDimitry Andric
4932cab237bSDimitry Andric bool shouldExportConstantsAsAbsoluteSymbols();
4942cab237bSDimitry Andric
4957a7e6055SDimitry Andric // This function is called during the export phase to create a symbol
4967a7e6055SDimitry Andric // definition containing information about the given vtable slot and list of
4977a7e6055SDimitry Andric // arguments.
4987a7e6055SDimitry Andric void exportGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args, StringRef Name,
4997a7e6055SDimitry Andric Constant *C);
5002cab237bSDimitry Andric void exportConstant(VTableSlot Slot, ArrayRef<uint64_t> Args, StringRef Name,
5012cab237bSDimitry Andric uint32_t Const, uint32_t &Storage);
5027a7e6055SDimitry Andric
5037a7e6055SDimitry Andric // This function is called during the import phase to create a reference to
5047a7e6055SDimitry Andric // the symbol definition created during the export phase.
5057a7e6055SDimitry Andric Constant *importGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args,
5062cab237bSDimitry Andric StringRef Name);
5072cab237bSDimitry Andric Constant *importConstant(VTableSlot Slot, ArrayRef<uint64_t> Args,
5082cab237bSDimitry Andric StringRef Name, IntegerType *IntTy,
5092cab237bSDimitry Andric uint32_t Storage);
5107a7e6055SDimitry Andric
5114ba319b5SDimitry Andric Constant *getMemberAddr(const TypeMemberInfo *M);
5124ba319b5SDimitry Andric
5137a7e6055SDimitry Andric void applyUniqueRetValOpt(CallSiteInfo &CSInfo, StringRef FnName, bool IsOne,
5147a7e6055SDimitry Andric Constant *UniqueMemberAddr);
5153ca95b02SDimitry Andric bool tryUniqueRetValOpt(unsigned BitWidth,
516d88c1a5aSDimitry Andric MutableArrayRef<VirtualCallTarget> TargetsForSlot,
5177a7e6055SDimitry Andric CallSiteInfo &CSInfo,
5187a7e6055SDimitry Andric WholeProgramDevirtResolution::ByArg *Res,
5197a7e6055SDimitry Andric VTableSlot Slot, ArrayRef<uint64_t> Args);
5207a7e6055SDimitry Andric
5217a7e6055SDimitry Andric void applyVirtualConstProp(CallSiteInfo &CSInfo, StringRef FnName,
5227a7e6055SDimitry Andric Constant *Byte, Constant *Bit);
5233ca95b02SDimitry Andric bool tryVirtualConstProp(MutableArrayRef<VirtualCallTarget> TargetsForSlot,
5247a7e6055SDimitry Andric VTableSlotInfo &SlotInfo,
5257a7e6055SDimitry Andric WholeProgramDevirtResolution *Res, VTableSlot Slot);
5263ca95b02SDimitry Andric
5273ca95b02SDimitry Andric void rebuildGlobal(VTableBits &B);
5283ca95b02SDimitry Andric
5297a7e6055SDimitry Andric // Apply the summary resolution for Slot to all virtual calls in SlotInfo.
5307a7e6055SDimitry Andric void importResolution(VTableSlot Slot, VTableSlotInfo &SlotInfo);
5317a7e6055SDimitry Andric
5327a7e6055SDimitry Andric // If we were able to eliminate all unsafe uses for a type checked load,
5337a7e6055SDimitry Andric // eliminate the associated type tests by replacing them with true.
5347a7e6055SDimitry Andric void removeRedundantTypeTests();
5357a7e6055SDimitry Andric
5363ca95b02SDimitry Andric bool run();
5377a7e6055SDimitry Andric
5387a7e6055SDimitry Andric // Lower the module using the action and summary passed as command line
5397a7e6055SDimitry Andric // arguments. For testing purposes only.
540*b5893f02SDimitry Andric static bool
541*b5893f02SDimitry Andric runForTesting(Module &M, function_ref<AAResults &(Function &)> AARGetter,
542*b5893f02SDimitry Andric function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter,
543*b5893f02SDimitry Andric function_ref<DominatorTree &(Function &)> LookupDomTree);
5443ca95b02SDimitry Andric };
5453ca95b02SDimitry Andric
5463ca95b02SDimitry Andric struct WholeProgramDevirt : public ModulePass {
5473ca95b02SDimitry Andric static char ID;
548d88c1a5aSDimitry Andric
5497a7e6055SDimitry Andric bool UseCommandLine = false;
5507a7e6055SDimitry Andric
5517a7e6055SDimitry Andric ModuleSummaryIndex *ExportSummary;
5527a7e6055SDimitry Andric const ModuleSummaryIndex *ImportSummary;
5537a7e6055SDimitry Andric
WholeProgramDevirt__anon3453c20f0211::WholeProgramDevirt5547a7e6055SDimitry Andric WholeProgramDevirt() : ModulePass(ID), UseCommandLine(true) {
5557a7e6055SDimitry Andric initializeWholeProgramDevirtPass(*PassRegistry::getPassRegistry());
5567a7e6055SDimitry Andric }
5577a7e6055SDimitry Andric
WholeProgramDevirt__anon3453c20f0211::WholeProgramDevirt5587a7e6055SDimitry Andric WholeProgramDevirt(ModuleSummaryIndex *ExportSummary,
5597a7e6055SDimitry Andric const ModuleSummaryIndex *ImportSummary)
5607a7e6055SDimitry Andric : ModulePass(ID), ExportSummary(ExportSummary),
5617a7e6055SDimitry Andric ImportSummary(ImportSummary) {
5623ca95b02SDimitry Andric initializeWholeProgramDevirtPass(*PassRegistry::getPassRegistry());
5633ca95b02SDimitry Andric }
564d88c1a5aSDimitry Andric
runOnModule__anon3453c20f0211::WholeProgramDevirt565d88c1a5aSDimitry Andric bool runOnModule(Module &M) override {
5663ca95b02SDimitry Andric if (skipModule(M))
5673ca95b02SDimitry Andric return false;
5682cab237bSDimitry Andric
5694ba319b5SDimitry Andric // In the new pass manager, we can request the optimization
5704ba319b5SDimitry Andric // remark emitter pass on a per-function-basis, which the
5714ba319b5SDimitry Andric // OREGetter will do for us.
5724ba319b5SDimitry Andric // In the old pass manager, this is harder, so we just build
5734ba319b5SDimitry Andric // an optimization remark emitter on the fly, when we need it.
5744ba319b5SDimitry Andric std::unique_ptr<OptimizationRemarkEmitter> ORE;
5754ba319b5SDimitry Andric auto OREGetter = [&](Function *F) -> OptimizationRemarkEmitter & {
5764ba319b5SDimitry Andric ORE = make_unique<OptimizationRemarkEmitter>(F);
5774ba319b5SDimitry Andric return *ORE;
5784ba319b5SDimitry Andric };
5792cab237bSDimitry Andric
580*b5893f02SDimitry Andric auto LookupDomTree = [this](Function &F) -> DominatorTree & {
581*b5893f02SDimitry Andric return this->getAnalysis<DominatorTreeWrapperPass>(F).getDomTree();
582*b5893f02SDimitry Andric };
5832cab237bSDimitry Andric
584*b5893f02SDimitry Andric if (UseCommandLine)
585*b5893f02SDimitry Andric return DevirtModule::runForTesting(M, LegacyAARGetter(*this), OREGetter,
586*b5893f02SDimitry Andric LookupDomTree);
587*b5893f02SDimitry Andric
588*b5893f02SDimitry Andric return DevirtModule(M, LegacyAARGetter(*this), OREGetter, LookupDomTree,
589*b5893f02SDimitry Andric ExportSummary, ImportSummary)
5907a7e6055SDimitry Andric .run();
5917a7e6055SDimitry Andric }
5923ca95b02SDimitry Andric
getAnalysisUsage__anon3453c20f0211::WholeProgramDevirt5937a7e6055SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override {
5947a7e6055SDimitry Andric AU.addRequired<AssumptionCacheTracker>();
5957a7e6055SDimitry Andric AU.addRequired<TargetLibraryInfoWrapperPass>();
596*b5893f02SDimitry Andric AU.addRequired<DominatorTreeWrapperPass>();
5973ca95b02SDimitry Andric }
5983ca95b02SDimitry Andric };
5993ca95b02SDimitry Andric
600d88c1a5aSDimitry Andric } // end anonymous namespace
6013ca95b02SDimitry Andric
6027a7e6055SDimitry Andric INITIALIZE_PASS_BEGIN(WholeProgramDevirt, "wholeprogramdevirt",
6037a7e6055SDimitry Andric "Whole program devirtualization", false, false)
6047a7e6055SDimitry Andric INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker)
6057a7e6055SDimitry Andric INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
606*b5893f02SDimitry Andric INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
6077a7e6055SDimitry Andric INITIALIZE_PASS_END(WholeProgramDevirt, "wholeprogramdevirt",
6083ca95b02SDimitry Andric "Whole program devirtualization", false, false)
6093ca95b02SDimitry Andric char WholeProgramDevirt::ID = 0;
6103ca95b02SDimitry Andric
6117a7e6055SDimitry Andric ModulePass *
createWholeProgramDevirtPass(ModuleSummaryIndex * ExportSummary,const ModuleSummaryIndex * ImportSummary)6127a7e6055SDimitry Andric llvm::createWholeProgramDevirtPass(ModuleSummaryIndex *ExportSummary,
6137a7e6055SDimitry Andric const ModuleSummaryIndex *ImportSummary) {
6147a7e6055SDimitry Andric return new WholeProgramDevirt(ExportSummary, ImportSummary);
6153ca95b02SDimitry Andric }
6163ca95b02SDimitry Andric
run(Module & M,ModuleAnalysisManager & AM)6173ca95b02SDimitry Andric PreservedAnalyses WholeProgramDevirtPass::run(Module &M,
6187a7e6055SDimitry Andric ModuleAnalysisManager &AM) {
6197a7e6055SDimitry Andric auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
6207a7e6055SDimitry Andric auto AARGetter = [&](Function &F) -> AAResults & {
6217a7e6055SDimitry Andric return FAM.getResult<AAManager>(F);
6227a7e6055SDimitry Andric };
6232cab237bSDimitry Andric auto OREGetter = [&](Function *F) -> OptimizationRemarkEmitter & {
6242cab237bSDimitry Andric return FAM.getResult<OptimizationRemarkEmitterAnalysis>(*F);
6252cab237bSDimitry Andric };
626*b5893f02SDimitry Andric auto LookupDomTree = [&FAM](Function &F) -> DominatorTree & {
627*b5893f02SDimitry Andric return FAM.getResult<DominatorTreeAnalysis>(F);
628*b5893f02SDimitry Andric };
629*b5893f02SDimitry Andric if (!DevirtModule(M, AARGetter, OREGetter, LookupDomTree, ExportSummary,
630*b5893f02SDimitry Andric ImportSummary)
6314ba319b5SDimitry Andric .run())
6323ca95b02SDimitry Andric return PreservedAnalyses::all();
6333ca95b02SDimitry Andric return PreservedAnalyses::none();
6343ca95b02SDimitry Andric }
6353ca95b02SDimitry Andric
runForTesting(Module & M,function_ref<AAResults & (Function &)> AARGetter,function_ref<OptimizationRemarkEmitter & (Function *)> OREGetter,function_ref<DominatorTree & (Function &)> LookupDomTree)6367a7e6055SDimitry Andric bool DevirtModule::runForTesting(
6372cab237bSDimitry Andric Module &M, function_ref<AAResults &(Function &)> AARGetter,
638*b5893f02SDimitry Andric function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter,
639*b5893f02SDimitry Andric function_ref<DominatorTree &(Function &)> LookupDomTree) {
6404ba319b5SDimitry Andric ModuleSummaryIndex Summary(/*HaveGVs=*/false);
6417a7e6055SDimitry Andric
6427a7e6055SDimitry Andric // Handle the command-line summary arguments. This code is for testing
6437a7e6055SDimitry Andric // purposes only, so we handle errors directly.
6447a7e6055SDimitry Andric if (!ClReadSummary.empty()) {
6457a7e6055SDimitry Andric ExitOnError ExitOnErr("-wholeprogramdevirt-read-summary: " + ClReadSummary +
6467a7e6055SDimitry Andric ": ");
6477a7e6055SDimitry Andric auto ReadSummaryFile =
6487a7e6055SDimitry Andric ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(ClReadSummary)));
6497a7e6055SDimitry Andric
6507a7e6055SDimitry Andric yaml::Input In(ReadSummaryFile->getBuffer());
6517a7e6055SDimitry Andric In >> Summary;
6527a7e6055SDimitry Andric ExitOnErr(errorCodeToError(In.error()));
6537a7e6055SDimitry Andric }
6547a7e6055SDimitry Andric
6557a7e6055SDimitry Andric bool Changed =
6567a7e6055SDimitry Andric DevirtModule(
657*b5893f02SDimitry Andric M, AARGetter, OREGetter, LookupDomTree,
6587a7e6055SDimitry Andric ClSummaryAction == PassSummaryAction::Export ? &Summary : nullptr,
6597a7e6055SDimitry Andric ClSummaryAction == PassSummaryAction::Import ? &Summary : nullptr)
6607a7e6055SDimitry Andric .run();
6617a7e6055SDimitry Andric
6627a7e6055SDimitry Andric if (!ClWriteSummary.empty()) {
6637a7e6055SDimitry Andric ExitOnError ExitOnErr(
6647a7e6055SDimitry Andric "-wholeprogramdevirt-write-summary: " + ClWriteSummary + ": ");
6657a7e6055SDimitry Andric std::error_code EC;
6667a7e6055SDimitry Andric raw_fd_ostream OS(ClWriteSummary, EC, sys::fs::F_Text);
6677a7e6055SDimitry Andric ExitOnErr(errorCodeToError(EC));
6687a7e6055SDimitry Andric
6697a7e6055SDimitry Andric yaml::Output Out(OS);
6707a7e6055SDimitry Andric Out << Summary;
6717a7e6055SDimitry Andric }
6727a7e6055SDimitry Andric
6737a7e6055SDimitry Andric return Changed;
6747a7e6055SDimitry Andric }
6757a7e6055SDimitry Andric
buildTypeIdentifierMap(std::vector<VTableBits> & Bits,DenseMap<Metadata *,std::set<TypeMemberInfo>> & TypeIdMap)6763ca95b02SDimitry Andric void DevirtModule::buildTypeIdentifierMap(
6773ca95b02SDimitry Andric std::vector<VTableBits> &Bits,
6783ca95b02SDimitry Andric DenseMap<Metadata *, std::set<TypeMemberInfo>> &TypeIdMap) {
6793ca95b02SDimitry Andric DenseMap<GlobalVariable *, VTableBits *> GVToBits;
6803ca95b02SDimitry Andric Bits.reserve(M.getGlobalList().size());
6813ca95b02SDimitry Andric SmallVector<MDNode *, 2> Types;
6823ca95b02SDimitry Andric for (GlobalVariable &GV : M.globals()) {
6833ca95b02SDimitry Andric Types.clear();
6843ca95b02SDimitry Andric GV.getMetadata(LLVMContext::MD_type, Types);
685*b5893f02SDimitry Andric if (GV.isDeclaration() || Types.empty())
6863ca95b02SDimitry Andric continue;
6873ca95b02SDimitry Andric
6883ca95b02SDimitry Andric VTableBits *&BitsPtr = GVToBits[&GV];
6893ca95b02SDimitry Andric if (!BitsPtr) {
6903ca95b02SDimitry Andric Bits.emplace_back();
6913ca95b02SDimitry Andric Bits.back().GV = &GV;
6923ca95b02SDimitry Andric Bits.back().ObjectSize =
6933ca95b02SDimitry Andric M.getDataLayout().getTypeAllocSize(GV.getInitializer()->getType());
6943ca95b02SDimitry Andric BitsPtr = &Bits.back();
6953ca95b02SDimitry Andric }
6963ca95b02SDimitry Andric
6973ca95b02SDimitry Andric for (MDNode *Type : Types) {
6983ca95b02SDimitry Andric auto TypeID = Type->getOperand(1).get();
6993ca95b02SDimitry Andric
7003ca95b02SDimitry Andric uint64_t Offset =
7013ca95b02SDimitry Andric cast<ConstantInt>(
7023ca95b02SDimitry Andric cast<ConstantAsMetadata>(Type->getOperand(0))->getValue())
7033ca95b02SDimitry Andric ->getZExtValue();
7043ca95b02SDimitry Andric
7053ca95b02SDimitry Andric TypeIdMap[TypeID].insert({BitsPtr, Offset});
7063ca95b02SDimitry Andric }
7073ca95b02SDimitry Andric }
7083ca95b02SDimitry Andric }
7093ca95b02SDimitry Andric
getPointerAtOffset(Constant * I,uint64_t Offset)710d88c1a5aSDimitry Andric Constant *DevirtModule::getPointerAtOffset(Constant *I, uint64_t Offset) {
711d88c1a5aSDimitry Andric if (I->getType()->isPointerTy()) {
712d88c1a5aSDimitry Andric if (Offset == 0)
713d88c1a5aSDimitry Andric return I;
714d88c1a5aSDimitry Andric return nullptr;
715d88c1a5aSDimitry Andric }
716d88c1a5aSDimitry Andric
717d88c1a5aSDimitry Andric const DataLayout &DL = M.getDataLayout();
718d88c1a5aSDimitry Andric
719d88c1a5aSDimitry Andric if (auto *C = dyn_cast<ConstantStruct>(I)) {
720d88c1a5aSDimitry Andric const StructLayout *SL = DL.getStructLayout(C->getType());
721d88c1a5aSDimitry Andric if (Offset >= SL->getSizeInBytes())
722d88c1a5aSDimitry Andric return nullptr;
723d88c1a5aSDimitry Andric
724d88c1a5aSDimitry Andric unsigned Op = SL->getElementContainingOffset(Offset);
725d88c1a5aSDimitry Andric return getPointerAtOffset(cast<Constant>(I->getOperand(Op)),
726d88c1a5aSDimitry Andric Offset - SL->getElementOffset(Op));
727d88c1a5aSDimitry Andric }
728d88c1a5aSDimitry Andric if (auto *C = dyn_cast<ConstantArray>(I)) {
729d88c1a5aSDimitry Andric ArrayType *VTableTy = C->getType();
730d88c1a5aSDimitry Andric uint64_t ElemSize = DL.getTypeAllocSize(VTableTy->getElementType());
731d88c1a5aSDimitry Andric
732d88c1a5aSDimitry Andric unsigned Op = Offset / ElemSize;
733d88c1a5aSDimitry Andric if (Op >= C->getNumOperands())
734d88c1a5aSDimitry Andric return nullptr;
735d88c1a5aSDimitry Andric
736d88c1a5aSDimitry Andric return getPointerAtOffset(cast<Constant>(I->getOperand(Op)),
737d88c1a5aSDimitry Andric Offset % ElemSize);
738d88c1a5aSDimitry Andric }
739d88c1a5aSDimitry Andric return nullptr;
740d88c1a5aSDimitry Andric }
741d88c1a5aSDimitry Andric
tryFindVirtualCallTargets(std::vector<VirtualCallTarget> & TargetsForSlot,const std::set<TypeMemberInfo> & TypeMemberInfos,uint64_t ByteOffset)7423ca95b02SDimitry Andric bool DevirtModule::tryFindVirtualCallTargets(
7433ca95b02SDimitry Andric std::vector<VirtualCallTarget> &TargetsForSlot,
7443ca95b02SDimitry Andric const std::set<TypeMemberInfo> &TypeMemberInfos, uint64_t ByteOffset) {
7453ca95b02SDimitry Andric for (const TypeMemberInfo &TM : TypeMemberInfos) {
7463ca95b02SDimitry Andric if (!TM.Bits->GV->isConstant())
7473ca95b02SDimitry Andric return false;
7483ca95b02SDimitry Andric
749d88c1a5aSDimitry Andric Constant *Ptr = getPointerAtOffset(TM.Bits->GV->getInitializer(),
750d88c1a5aSDimitry Andric TM.Offset + ByteOffset);
751d88c1a5aSDimitry Andric if (!Ptr)
7523ca95b02SDimitry Andric return false;
7533ca95b02SDimitry Andric
754d88c1a5aSDimitry Andric auto Fn = dyn_cast<Function>(Ptr->stripPointerCasts());
7553ca95b02SDimitry Andric if (!Fn)
7563ca95b02SDimitry Andric return false;
7573ca95b02SDimitry Andric
7583ca95b02SDimitry Andric // We can disregard __cxa_pure_virtual as a possible call target, as
7593ca95b02SDimitry Andric // calls to pure virtuals are UB.
7603ca95b02SDimitry Andric if (Fn->getName() == "__cxa_pure_virtual")
7613ca95b02SDimitry Andric continue;
7623ca95b02SDimitry Andric
7633ca95b02SDimitry Andric TargetsForSlot.push_back({Fn, &TM});
7643ca95b02SDimitry Andric }
7653ca95b02SDimitry Andric
7663ca95b02SDimitry Andric // Give up if we couldn't find any targets.
7673ca95b02SDimitry Andric return !TargetsForSlot.empty();
7683ca95b02SDimitry Andric }
7693ca95b02SDimitry Andric
applySingleImplDevirt(VTableSlotInfo & SlotInfo,Constant * TheFn,bool & IsExported)7707a7e6055SDimitry Andric void DevirtModule::applySingleImplDevirt(VTableSlotInfo &SlotInfo,
7717a7e6055SDimitry Andric Constant *TheFn, bool &IsExported) {
7727a7e6055SDimitry Andric auto Apply = [&](CallSiteInfo &CSInfo) {
7737a7e6055SDimitry Andric for (auto &&VCallSite : CSInfo.CallSites) {
774d88c1a5aSDimitry Andric if (RemarksEnabled)
775*b5893f02SDimitry Andric VCallSite.emitRemark("single-impl",
776*b5893f02SDimitry Andric TheFn->stripPointerCasts()->getName(), OREGetter);
7773ca95b02SDimitry Andric VCallSite.CS.setCalledFunction(ConstantExpr::getBitCast(
7783ca95b02SDimitry Andric TheFn, VCallSite.CS.getCalledValue()->getType()));
7793ca95b02SDimitry Andric // This use is no longer unsafe.
7803ca95b02SDimitry Andric if (VCallSite.NumUnsafeUses)
7813ca95b02SDimitry Andric --*VCallSite.NumUnsafeUses;
7823ca95b02SDimitry Andric }
7834ba319b5SDimitry Andric if (CSInfo.isExported())
7847a7e6055SDimitry Andric IsExported = true;
7857a7e6055SDimitry Andric CSInfo.markDevirt();
7867a7e6055SDimitry Andric };
7877a7e6055SDimitry Andric Apply(SlotInfo.CSInfo);
7887a7e6055SDimitry Andric for (auto &P : SlotInfo.ConstCSInfo)
7897a7e6055SDimitry Andric Apply(P.second);
7907a7e6055SDimitry Andric }
7917a7e6055SDimitry Andric
trySingleImplDevirt(MutableArrayRef<VirtualCallTarget> TargetsForSlot,VTableSlotInfo & SlotInfo,WholeProgramDevirtResolution * Res)7927a7e6055SDimitry Andric bool DevirtModule::trySingleImplDevirt(
7937a7e6055SDimitry Andric MutableArrayRef<VirtualCallTarget> TargetsForSlot,
7947a7e6055SDimitry Andric VTableSlotInfo &SlotInfo, WholeProgramDevirtResolution *Res) {
7957a7e6055SDimitry Andric // See if the program contains a single implementation of this virtual
7967a7e6055SDimitry Andric // function.
7977a7e6055SDimitry Andric Function *TheFn = TargetsForSlot[0].Fn;
7987a7e6055SDimitry Andric for (auto &&Target : TargetsForSlot)
7997a7e6055SDimitry Andric if (TheFn != Target.Fn)
8007a7e6055SDimitry Andric return false;
8017a7e6055SDimitry Andric
8027a7e6055SDimitry Andric // If so, update each call site to call that implementation directly.
8037a7e6055SDimitry Andric if (RemarksEnabled)
8047a7e6055SDimitry Andric TargetsForSlot[0].WasDevirt = true;
8057a7e6055SDimitry Andric
8067a7e6055SDimitry Andric bool IsExported = false;
8077a7e6055SDimitry Andric applySingleImplDevirt(SlotInfo, TheFn, IsExported);
8087a7e6055SDimitry Andric if (!IsExported)
8097a7e6055SDimitry Andric return false;
8107a7e6055SDimitry Andric
8117a7e6055SDimitry Andric // If the only implementation has local linkage, we must promote to external
8127a7e6055SDimitry Andric // to make it visible to thin LTO objects. We can only get here during the
8137a7e6055SDimitry Andric // ThinLTO export phase.
8147a7e6055SDimitry Andric if (TheFn->hasLocalLinkage()) {
8152cab237bSDimitry Andric std::string NewName = (TheFn->getName() + "$merged").str();
8162cab237bSDimitry Andric
8172cab237bSDimitry Andric // Since we are renaming the function, any comdats with the same name must
8182cab237bSDimitry Andric // also be renamed. This is required when targeting COFF, as the comdat name
8192cab237bSDimitry Andric // must match one of the names of the symbols in the comdat.
8202cab237bSDimitry Andric if (Comdat *C = TheFn->getComdat()) {
8212cab237bSDimitry Andric if (C->getName() == TheFn->getName()) {
8222cab237bSDimitry Andric Comdat *NewC = M.getOrInsertComdat(NewName);
8232cab237bSDimitry Andric NewC->setSelectionKind(C->getSelectionKind());
8242cab237bSDimitry Andric for (GlobalObject &GO : M.global_objects())
8252cab237bSDimitry Andric if (GO.getComdat() == C)
8262cab237bSDimitry Andric GO.setComdat(NewC);
8272cab237bSDimitry Andric }
8282cab237bSDimitry Andric }
8292cab237bSDimitry Andric
8307a7e6055SDimitry Andric TheFn->setLinkage(GlobalValue::ExternalLinkage);
8317a7e6055SDimitry Andric TheFn->setVisibility(GlobalValue::HiddenVisibility);
8322cab237bSDimitry Andric TheFn->setName(NewName);
8337a7e6055SDimitry Andric }
8347a7e6055SDimitry Andric
8357a7e6055SDimitry Andric Res->TheKind = WholeProgramDevirtResolution::SingleImpl;
8367a7e6055SDimitry Andric Res->SingleImplName = TheFn->getName();
8377a7e6055SDimitry Andric
8383ca95b02SDimitry Andric return true;
8393ca95b02SDimitry Andric }
8403ca95b02SDimitry Andric
tryICallBranchFunnel(MutableArrayRef<VirtualCallTarget> TargetsForSlot,VTableSlotInfo & SlotInfo,WholeProgramDevirtResolution * Res,VTableSlot Slot)8414ba319b5SDimitry Andric void DevirtModule::tryICallBranchFunnel(
8424ba319b5SDimitry Andric MutableArrayRef<VirtualCallTarget> TargetsForSlot, VTableSlotInfo &SlotInfo,
8434ba319b5SDimitry Andric WholeProgramDevirtResolution *Res, VTableSlot Slot) {
8444ba319b5SDimitry Andric Triple T(M.getTargetTriple());
8454ba319b5SDimitry Andric if (T.getArch() != Triple::x86_64)
8464ba319b5SDimitry Andric return;
8474ba319b5SDimitry Andric
8484ba319b5SDimitry Andric if (TargetsForSlot.size() > ClThreshold)
8494ba319b5SDimitry Andric return;
8504ba319b5SDimitry Andric
8514ba319b5SDimitry Andric bool HasNonDevirt = !SlotInfo.CSInfo.AllCallSitesDevirted;
8524ba319b5SDimitry Andric if (!HasNonDevirt)
8534ba319b5SDimitry Andric for (auto &P : SlotInfo.ConstCSInfo)
8544ba319b5SDimitry Andric if (!P.second.AllCallSitesDevirted) {
8554ba319b5SDimitry Andric HasNonDevirt = true;
8564ba319b5SDimitry Andric break;
8574ba319b5SDimitry Andric }
8584ba319b5SDimitry Andric
8594ba319b5SDimitry Andric if (!HasNonDevirt)
8604ba319b5SDimitry Andric return;
8614ba319b5SDimitry Andric
8624ba319b5SDimitry Andric FunctionType *FT =
8634ba319b5SDimitry Andric FunctionType::get(Type::getVoidTy(M.getContext()), {Int8PtrTy}, true);
8644ba319b5SDimitry Andric Function *JT;
8654ba319b5SDimitry Andric if (isa<MDString>(Slot.TypeID)) {
8664ba319b5SDimitry Andric JT = Function::Create(FT, Function::ExternalLinkage,
867*b5893f02SDimitry Andric M.getDataLayout().getProgramAddressSpace(),
8684ba319b5SDimitry Andric getGlobalName(Slot, {}, "branch_funnel"), &M);
8694ba319b5SDimitry Andric JT->setVisibility(GlobalValue::HiddenVisibility);
8704ba319b5SDimitry Andric } else {
871*b5893f02SDimitry Andric JT = Function::Create(FT, Function::InternalLinkage,
872*b5893f02SDimitry Andric M.getDataLayout().getProgramAddressSpace(),
873*b5893f02SDimitry Andric "branch_funnel", &M);
8744ba319b5SDimitry Andric }
8754ba319b5SDimitry Andric JT->addAttribute(1, Attribute::Nest);
8764ba319b5SDimitry Andric
8774ba319b5SDimitry Andric std::vector<Value *> JTArgs;
8784ba319b5SDimitry Andric JTArgs.push_back(JT->arg_begin());
8794ba319b5SDimitry Andric for (auto &T : TargetsForSlot) {
8804ba319b5SDimitry Andric JTArgs.push_back(getMemberAddr(T.TM));
8814ba319b5SDimitry Andric JTArgs.push_back(T.Fn);
8824ba319b5SDimitry Andric }
8834ba319b5SDimitry Andric
8844ba319b5SDimitry Andric BasicBlock *BB = BasicBlock::Create(M.getContext(), "", JT, nullptr);
8854ba319b5SDimitry Andric Constant *Intr =
8864ba319b5SDimitry Andric Intrinsic::getDeclaration(&M, llvm::Intrinsic::icall_branch_funnel, {});
8874ba319b5SDimitry Andric
8884ba319b5SDimitry Andric auto *CI = CallInst::Create(Intr, JTArgs, "", BB);
8894ba319b5SDimitry Andric CI->setTailCallKind(CallInst::TCK_MustTail);
8904ba319b5SDimitry Andric ReturnInst::Create(M.getContext(), nullptr, BB);
8914ba319b5SDimitry Andric
8924ba319b5SDimitry Andric bool IsExported = false;
8934ba319b5SDimitry Andric applyICallBranchFunnel(SlotInfo, JT, IsExported);
8944ba319b5SDimitry Andric if (IsExported)
8954ba319b5SDimitry Andric Res->TheKind = WholeProgramDevirtResolution::BranchFunnel;
8964ba319b5SDimitry Andric }
8974ba319b5SDimitry Andric
applyICallBranchFunnel(VTableSlotInfo & SlotInfo,Constant * JT,bool & IsExported)8984ba319b5SDimitry Andric void DevirtModule::applyICallBranchFunnel(VTableSlotInfo &SlotInfo,
8994ba319b5SDimitry Andric Constant *JT, bool &IsExported) {
9004ba319b5SDimitry Andric auto Apply = [&](CallSiteInfo &CSInfo) {
9014ba319b5SDimitry Andric if (CSInfo.isExported())
9024ba319b5SDimitry Andric IsExported = true;
9034ba319b5SDimitry Andric if (CSInfo.AllCallSitesDevirted)
9044ba319b5SDimitry Andric return;
9054ba319b5SDimitry Andric for (auto &&VCallSite : CSInfo.CallSites) {
9064ba319b5SDimitry Andric CallSite CS = VCallSite.CS;
9074ba319b5SDimitry Andric
9084ba319b5SDimitry Andric // Jump tables are only profitable if the retpoline mitigation is enabled.
9094ba319b5SDimitry Andric Attribute FSAttr = CS.getCaller()->getFnAttribute("target-features");
9104ba319b5SDimitry Andric if (FSAttr.hasAttribute(Attribute::None) ||
9114ba319b5SDimitry Andric !FSAttr.getValueAsString().contains("+retpoline"))
9124ba319b5SDimitry Andric continue;
9134ba319b5SDimitry Andric
9144ba319b5SDimitry Andric if (RemarksEnabled)
915*b5893f02SDimitry Andric VCallSite.emitRemark("branch-funnel",
916*b5893f02SDimitry Andric JT->stripPointerCasts()->getName(), OREGetter);
9174ba319b5SDimitry Andric
9184ba319b5SDimitry Andric // Pass the address of the vtable in the nest register, which is r10 on
9194ba319b5SDimitry Andric // x86_64.
9204ba319b5SDimitry Andric std::vector<Type *> NewArgs;
9214ba319b5SDimitry Andric NewArgs.push_back(Int8PtrTy);
9224ba319b5SDimitry Andric for (Type *T : CS.getFunctionType()->params())
9234ba319b5SDimitry Andric NewArgs.push_back(T);
9244ba319b5SDimitry Andric PointerType *NewFT = PointerType::getUnqual(
9254ba319b5SDimitry Andric FunctionType::get(CS.getFunctionType()->getReturnType(), NewArgs,
9264ba319b5SDimitry Andric CS.getFunctionType()->isVarArg()));
9274ba319b5SDimitry Andric
9284ba319b5SDimitry Andric IRBuilder<> IRB(CS.getInstruction());
9294ba319b5SDimitry Andric std::vector<Value *> Args;
9304ba319b5SDimitry Andric Args.push_back(IRB.CreateBitCast(VCallSite.VTable, Int8PtrTy));
9314ba319b5SDimitry Andric for (unsigned I = 0; I != CS.getNumArgOperands(); ++I)
9324ba319b5SDimitry Andric Args.push_back(CS.getArgOperand(I));
9334ba319b5SDimitry Andric
9344ba319b5SDimitry Andric CallSite NewCS;
9354ba319b5SDimitry Andric if (CS.isCall())
9364ba319b5SDimitry Andric NewCS = IRB.CreateCall(IRB.CreateBitCast(JT, NewFT), Args);
9374ba319b5SDimitry Andric else
9384ba319b5SDimitry Andric NewCS = IRB.CreateInvoke(
9394ba319b5SDimitry Andric IRB.CreateBitCast(JT, NewFT),
9404ba319b5SDimitry Andric cast<InvokeInst>(CS.getInstruction())->getNormalDest(),
9414ba319b5SDimitry Andric cast<InvokeInst>(CS.getInstruction())->getUnwindDest(), Args);
9424ba319b5SDimitry Andric NewCS.setCallingConv(CS.getCallingConv());
9434ba319b5SDimitry Andric
9444ba319b5SDimitry Andric AttributeList Attrs = CS.getAttributes();
9454ba319b5SDimitry Andric std::vector<AttributeSet> NewArgAttrs;
9464ba319b5SDimitry Andric NewArgAttrs.push_back(AttributeSet::get(
9474ba319b5SDimitry Andric M.getContext(), ArrayRef<Attribute>{Attribute::get(
9484ba319b5SDimitry Andric M.getContext(), Attribute::Nest)}));
9494ba319b5SDimitry Andric for (unsigned I = 0; I + 2 < Attrs.getNumAttrSets(); ++I)
9504ba319b5SDimitry Andric NewArgAttrs.push_back(Attrs.getParamAttributes(I));
9514ba319b5SDimitry Andric NewCS.setAttributes(
9524ba319b5SDimitry Andric AttributeList::get(M.getContext(), Attrs.getFnAttributes(),
9534ba319b5SDimitry Andric Attrs.getRetAttributes(), NewArgAttrs));
9544ba319b5SDimitry Andric
9554ba319b5SDimitry Andric CS->replaceAllUsesWith(NewCS.getInstruction());
9564ba319b5SDimitry Andric CS->eraseFromParent();
9574ba319b5SDimitry Andric
9584ba319b5SDimitry Andric // This use is no longer unsafe.
9594ba319b5SDimitry Andric if (VCallSite.NumUnsafeUses)
9604ba319b5SDimitry Andric --*VCallSite.NumUnsafeUses;
9614ba319b5SDimitry Andric }
9624ba319b5SDimitry Andric // Don't mark as devirtualized because there may be callers compiled without
9634ba319b5SDimitry Andric // retpoline mitigation, which would mean that they are lowered to
9644ba319b5SDimitry Andric // llvm.type.test and therefore require an llvm.type.test resolution for the
9654ba319b5SDimitry Andric // type identifier.
9664ba319b5SDimitry Andric };
9674ba319b5SDimitry Andric Apply(SlotInfo.CSInfo);
9684ba319b5SDimitry Andric for (auto &P : SlotInfo.ConstCSInfo)
9694ba319b5SDimitry Andric Apply(P.second);
9704ba319b5SDimitry Andric }
9714ba319b5SDimitry Andric
tryEvaluateFunctionsWithArgs(MutableArrayRef<VirtualCallTarget> TargetsForSlot,ArrayRef<uint64_t> Args)9723ca95b02SDimitry Andric bool DevirtModule::tryEvaluateFunctionsWithArgs(
9733ca95b02SDimitry Andric MutableArrayRef<VirtualCallTarget> TargetsForSlot,
9747a7e6055SDimitry Andric ArrayRef<uint64_t> Args) {
9753ca95b02SDimitry Andric // Evaluate each function and store the result in each target's RetVal
9763ca95b02SDimitry Andric // field.
9773ca95b02SDimitry Andric for (VirtualCallTarget &Target : TargetsForSlot) {
9783ca95b02SDimitry Andric if (Target.Fn->arg_size() != Args.size() + 1)
9793ca95b02SDimitry Andric return false;
9803ca95b02SDimitry Andric
9813ca95b02SDimitry Andric Evaluator Eval(M.getDataLayout(), nullptr);
9823ca95b02SDimitry Andric SmallVector<Constant *, 2> EvalArgs;
9833ca95b02SDimitry Andric EvalArgs.push_back(
9843ca95b02SDimitry Andric Constant::getNullValue(Target.Fn->getFunctionType()->getParamType(0)));
9857a7e6055SDimitry Andric for (unsigned I = 0; I != Args.size(); ++I) {
9867a7e6055SDimitry Andric auto *ArgTy = dyn_cast<IntegerType>(
9877a7e6055SDimitry Andric Target.Fn->getFunctionType()->getParamType(I + 1));
9887a7e6055SDimitry Andric if (!ArgTy)
9897a7e6055SDimitry Andric return false;
9907a7e6055SDimitry Andric EvalArgs.push_back(ConstantInt::get(ArgTy, Args[I]));
9917a7e6055SDimitry Andric }
9927a7e6055SDimitry Andric
9933ca95b02SDimitry Andric Constant *RetVal;
9943ca95b02SDimitry Andric if (!Eval.EvaluateFunction(Target.Fn, RetVal, EvalArgs) ||
9953ca95b02SDimitry Andric !isa<ConstantInt>(RetVal))
9963ca95b02SDimitry Andric return false;
9973ca95b02SDimitry Andric Target.RetVal = cast<ConstantInt>(RetVal)->getZExtValue();
9983ca95b02SDimitry Andric }
9993ca95b02SDimitry Andric return true;
10003ca95b02SDimitry Andric }
10013ca95b02SDimitry Andric
applyUniformRetValOpt(CallSiteInfo & CSInfo,StringRef FnName,uint64_t TheRetVal)10027a7e6055SDimitry Andric void DevirtModule::applyUniformRetValOpt(CallSiteInfo &CSInfo, StringRef FnName,
10037a7e6055SDimitry Andric uint64_t TheRetVal) {
10047a7e6055SDimitry Andric for (auto Call : CSInfo.CallSites)
10057a7e6055SDimitry Andric Call.replaceAndErase(
10062cab237bSDimitry Andric "uniform-ret-val", FnName, RemarksEnabled, OREGetter,
10077a7e6055SDimitry Andric ConstantInt::get(cast<IntegerType>(Call.CS.getType()), TheRetVal));
10087a7e6055SDimitry Andric CSInfo.markDevirt();
10097a7e6055SDimitry Andric }
10107a7e6055SDimitry Andric
tryUniformRetValOpt(MutableArrayRef<VirtualCallTarget> TargetsForSlot,CallSiteInfo & CSInfo,WholeProgramDevirtResolution::ByArg * Res)10113ca95b02SDimitry Andric bool DevirtModule::tryUniformRetValOpt(
10127a7e6055SDimitry Andric MutableArrayRef<VirtualCallTarget> TargetsForSlot, CallSiteInfo &CSInfo,
10137a7e6055SDimitry Andric WholeProgramDevirtResolution::ByArg *Res) {
10143ca95b02SDimitry Andric // Uniform return value optimization. If all functions return the same
10153ca95b02SDimitry Andric // constant, replace all calls with that constant.
10163ca95b02SDimitry Andric uint64_t TheRetVal = TargetsForSlot[0].RetVal;
10173ca95b02SDimitry Andric for (const VirtualCallTarget &Target : TargetsForSlot)
10183ca95b02SDimitry Andric if (Target.RetVal != TheRetVal)
10193ca95b02SDimitry Andric return false;
10203ca95b02SDimitry Andric
10217a7e6055SDimitry Andric if (CSInfo.isExported()) {
10227a7e6055SDimitry Andric Res->TheKind = WholeProgramDevirtResolution::ByArg::UniformRetVal;
10237a7e6055SDimitry Andric Res->Info = TheRetVal;
10247a7e6055SDimitry Andric }
10257a7e6055SDimitry Andric
10267a7e6055SDimitry Andric applyUniformRetValOpt(CSInfo, TargetsForSlot[0].Fn->getName(), TheRetVal);
1027d88c1a5aSDimitry Andric if (RemarksEnabled)
1028d88c1a5aSDimitry Andric for (auto &&Target : TargetsForSlot)
1029d88c1a5aSDimitry Andric Target.WasDevirt = true;
10303ca95b02SDimitry Andric return true;
10313ca95b02SDimitry Andric }
10323ca95b02SDimitry Andric
getGlobalName(VTableSlot Slot,ArrayRef<uint64_t> Args,StringRef Name)10337a7e6055SDimitry Andric std::string DevirtModule::getGlobalName(VTableSlot Slot,
10347a7e6055SDimitry Andric ArrayRef<uint64_t> Args,
10357a7e6055SDimitry Andric StringRef Name) {
10367a7e6055SDimitry Andric std::string FullName = "__typeid_";
10377a7e6055SDimitry Andric raw_string_ostream OS(FullName);
10387a7e6055SDimitry Andric OS << cast<MDString>(Slot.TypeID)->getString() << '_' << Slot.ByteOffset;
10397a7e6055SDimitry Andric for (uint64_t Arg : Args)
10407a7e6055SDimitry Andric OS << '_' << Arg;
10417a7e6055SDimitry Andric OS << '_' << Name;
10427a7e6055SDimitry Andric return OS.str();
10437a7e6055SDimitry Andric }
10447a7e6055SDimitry Andric
shouldExportConstantsAsAbsoluteSymbols()10452cab237bSDimitry Andric bool DevirtModule::shouldExportConstantsAsAbsoluteSymbols() {
10462cab237bSDimitry Andric Triple T(M.getTargetTriple());
10472cab237bSDimitry Andric return (T.getArch() == Triple::x86 || T.getArch() == Triple::x86_64) &&
10482cab237bSDimitry Andric T.getObjectFormat() == Triple::ELF;
10492cab237bSDimitry Andric }
10502cab237bSDimitry Andric
exportGlobal(VTableSlot Slot,ArrayRef<uint64_t> Args,StringRef Name,Constant * C)10517a7e6055SDimitry Andric void DevirtModule::exportGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args,
10527a7e6055SDimitry Andric StringRef Name, Constant *C) {
10537a7e6055SDimitry Andric GlobalAlias *GA = GlobalAlias::create(Int8Ty, 0, GlobalValue::ExternalLinkage,
10547a7e6055SDimitry Andric getGlobalName(Slot, Args, Name), C, &M);
10557a7e6055SDimitry Andric GA->setVisibility(GlobalValue::HiddenVisibility);
10567a7e6055SDimitry Andric }
10577a7e6055SDimitry Andric
exportConstant(VTableSlot Slot,ArrayRef<uint64_t> Args,StringRef Name,uint32_t Const,uint32_t & Storage)10582cab237bSDimitry Andric void DevirtModule::exportConstant(VTableSlot Slot, ArrayRef<uint64_t> Args,
10592cab237bSDimitry Andric StringRef Name, uint32_t Const,
10602cab237bSDimitry Andric uint32_t &Storage) {
10612cab237bSDimitry Andric if (shouldExportConstantsAsAbsoluteSymbols()) {
10622cab237bSDimitry Andric exportGlobal(
10632cab237bSDimitry Andric Slot, Args, Name,
10642cab237bSDimitry Andric ConstantExpr::getIntToPtr(ConstantInt::get(Int32Ty, Const), Int8PtrTy));
10652cab237bSDimitry Andric return;
10662cab237bSDimitry Andric }
10672cab237bSDimitry Andric
10682cab237bSDimitry Andric Storage = Const;
10692cab237bSDimitry Andric }
10702cab237bSDimitry Andric
importGlobal(VTableSlot Slot,ArrayRef<uint64_t> Args,StringRef Name)10717a7e6055SDimitry Andric Constant *DevirtModule::importGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args,
10722cab237bSDimitry Andric StringRef Name) {
10737a7e6055SDimitry Andric Constant *C = M.getOrInsertGlobal(getGlobalName(Slot, Args, Name), Int8Ty);
10747a7e6055SDimitry Andric auto *GV = dyn_cast<GlobalVariable>(C);
10752cab237bSDimitry Andric if (GV)
10762cab237bSDimitry Andric GV->setVisibility(GlobalValue::HiddenVisibility);
10772cab237bSDimitry Andric return C;
10782cab237bSDimitry Andric }
10792cab237bSDimitry Andric
importConstant(VTableSlot Slot,ArrayRef<uint64_t> Args,StringRef Name,IntegerType * IntTy,uint32_t Storage)10802cab237bSDimitry Andric Constant *DevirtModule::importConstant(VTableSlot Slot, ArrayRef<uint64_t> Args,
10812cab237bSDimitry Andric StringRef Name, IntegerType *IntTy,
10822cab237bSDimitry Andric uint32_t Storage) {
10832cab237bSDimitry Andric if (!shouldExportConstantsAsAbsoluteSymbols())
10842cab237bSDimitry Andric return ConstantInt::get(IntTy, Storage);
10852cab237bSDimitry Andric
10862cab237bSDimitry Andric Constant *C = importGlobal(Slot, Args, Name);
10872cab237bSDimitry Andric auto *GV = cast<GlobalVariable>(C->stripPointerCasts());
10882cab237bSDimitry Andric C = ConstantExpr::getPtrToInt(C, IntTy);
10892cab237bSDimitry Andric
10907a7e6055SDimitry Andric // We only need to set metadata if the global is newly created, in which
10917a7e6055SDimitry Andric // case it would not have hidden visibility.
10924ba319b5SDimitry Andric if (GV->hasMetadata(LLVMContext::MD_absolute_symbol))
10937a7e6055SDimitry Andric return C;
10947a7e6055SDimitry Andric
10957a7e6055SDimitry Andric auto SetAbsRange = [&](uint64_t Min, uint64_t Max) {
10967a7e6055SDimitry Andric auto *MinC = ConstantAsMetadata::get(ConstantInt::get(IntPtrTy, Min));
10977a7e6055SDimitry Andric auto *MaxC = ConstantAsMetadata::get(ConstantInt::get(IntPtrTy, Max));
10987a7e6055SDimitry Andric GV->setMetadata(LLVMContext::MD_absolute_symbol,
10997a7e6055SDimitry Andric MDNode::get(M.getContext(), {MinC, MaxC}));
11007a7e6055SDimitry Andric };
11012cab237bSDimitry Andric unsigned AbsWidth = IntTy->getBitWidth();
11027a7e6055SDimitry Andric if (AbsWidth == IntPtrTy->getBitWidth())
11037a7e6055SDimitry Andric SetAbsRange(~0ull, ~0ull); // Full set.
11042cab237bSDimitry Andric else
11057a7e6055SDimitry Andric SetAbsRange(0, 1ull << AbsWidth);
11062cab237bSDimitry Andric return C;
11077a7e6055SDimitry Andric }
11087a7e6055SDimitry Andric
applyUniqueRetValOpt(CallSiteInfo & CSInfo,StringRef FnName,bool IsOne,Constant * UniqueMemberAddr)11097a7e6055SDimitry Andric void DevirtModule::applyUniqueRetValOpt(CallSiteInfo &CSInfo, StringRef FnName,
11107a7e6055SDimitry Andric bool IsOne,
11117a7e6055SDimitry Andric Constant *UniqueMemberAddr) {
11127a7e6055SDimitry Andric for (auto &&Call : CSInfo.CallSites) {
11137a7e6055SDimitry Andric IRBuilder<> B(Call.CS.getInstruction());
11142cab237bSDimitry Andric Value *Cmp =
11152cab237bSDimitry Andric B.CreateICmp(IsOne ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE,
11162cab237bSDimitry Andric B.CreateBitCast(Call.VTable, Int8PtrTy), UniqueMemberAddr);
11177a7e6055SDimitry Andric Cmp = B.CreateZExt(Cmp, Call.CS->getType());
11182cab237bSDimitry Andric Call.replaceAndErase("unique-ret-val", FnName, RemarksEnabled, OREGetter,
11192cab237bSDimitry Andric Cmp);
11207a7e6055SDimitry Andric }
11217a7e6055SDimitry Andric CSInfo.markDevirt();
11227a7e6055SDimitry Andric }
11237a7e6055SDimitry Andric
getMemberAddr(const TypeMemberInfo * M)11244ba319b5SDimitry Andric Constant *DevirtModule::getMemberAddr(const TypeMemberInfo *M) {
11254ba319b5SDimitry Andric Constant *C = ConstantExpr::getBitCast(M->Bits->GV, Int8PtrTy);
11264ba319b5SDimitry Andric return ConstantExpr::getGetElementPtr(Int8Ty, C,
11274ba319b5SDimitry Andric ConstantInt::get(Int64Ty, M->Offset));
11284ba319b5SDimitry Andric }
11294ba319b5SDimitry Andric
tryUniqueRetValOpt(unsigned BitWidth,MutableArrayRef<VirtualCallTarget> TargetsForSlot,CallSiteInfo & CSInfo,WholeProgramDevirtResolution::ByArg * Res,VTableSlot Slot,ArrayRef<uint64_t> Args)11303ca95b02SDimitry Andric bool DevirtModule::tryUniqueRetValOpt(
1131d88c1a5aSDimitry Andric unsigned BitWidth, MutableArrayRef<VirtualCallTarget> TargetsForSlot,
11327a7e6055SDimitry Andric CallSiteInfo &CSInfo, WholeProgramDevirtResolution::ByArg *Res,
11337a7e6055SDimitry Andric VTableSlot Slot, ArrayRef<uint64_t> Args) {
11343ca95b02SDimitry Andric // IsOne controls whether we look for a 0 or a 1.
11353ca95b02SDimitry Andric auto tryUniqueRetValOptFor = [&](bool IsOne) {
1136d88c1a5aSDimitry Andric const TypeMemberInfo *UniqueMember = nullptr;
11373ca95b02SDimitry Andric for (const VirtualCallTarget &Target : TargetsForSlot) {
11383ca95b02SDimitry Andric if (Target.RetVal == (IsOne ? 1 : 0)) {
11393ca95b02SDimitry Andric if (UniqueMember)
11403ca95b02SDimitry Andric return false;
11413ca95b02SDimitry Andric UniqueMember = Target.TM;
11423ca95b02SDimitry Andric }
11433ca95b02SDimitry Andric }
11443ca95b02SDimitry Andric
11453ca95b02SDimitry Andric // We should have found a unique member or bailed out by now. We already
11463ca95b02SDimitry Andric // checked for a uniform return value in tryUniformRetValOpt.
11473ca95b02SDimitry Andric assert(UniqueMember);
11483ca95b02SDimitry Andric
11494ba319b5SDimitry Andric Constant *UniqueMemberAddr = getMemberAddr(UniqueMember);
11507a7e6055SDimitry Andric if (CSInfo.isExported()) {
11517a7e6055SDimitry Andric Res->TheKind = WholeProgramDevirtResolution::ByArg::UniqueRetVal;
11527a7e6055SDimitry Andric Res->Info = IsOne;
11537a7e6055SDimitry Andric
11547a7e6055SDimitry Andric exportGlobal(Slot, Args, "unique_member", UniqueMemberAddr);
11553ca95b02SDimitry Andric }
11567a7e6055SDimitry Andric
11577a7e6055SDimitry Andric // Replace each call with the comparison.
11587a7e6055SDimitry Andric applyUniqueRetValOpt(CSInfo, TargetsForSlot[0].Fn->getName(), IsOne,
11597a7e6055SDimitry Andric UniqueMemberAddr);
11607a7e6055SDimitry Andric
1161d88c1a5aSDimitry Andric // Update devirtualization statistics for targets.
1162d88c1a5aSDimitry Andric if (RemarksEnabled)
1163d88c1a5aSDimitry Andric for (auto &&Target : TargetsForSlot)
1164d88c1a5aSDimitry Andric Target.WasDevirt = true;
1165d88c1a5aSDimitry Andric
11663ca95b02SDimitry Andric return true;
11673ca95b02SDimitry Andric };
11683ca95b02SDimitry Andric
11693ca95b02SDimitry Andric if (BitWidth == 1) {
11703ca95b02SDimitry Andric if (tryUniqueRetValOptFor(true))
11713ca95b02SDimitry Andric return true;
11723ca95b02SDimitry Andric if (tryUniqueRetValOptFor(false))
11733ca95b02SDimitry Andric return true;
11743ca95b02SDimitry Andric }
11753ca95b02SDimitry Andric return false;
11763ca95b02SDimitry Andric }
11773ca95b02SDimitry Andric
applyVirtualConstProp(CallSiteInfo & CSInfo,StringRef FnName,Constant * Byte,Constant * Bit)11787a7e6055SDimitry Andric void DevirtModule::applyVirtualConstProp(CallSiteInfo &CSInfo, StringRef FnName,
11797a7e6055SDimitry Andric Constant *Byte, Constant *Bit) {
11807a7e6055SDimitry Andric for (auto Call : CSInfo.CallSites) {
11817a7e6055SDimitry Andric auto *RetType = cast<IntegerType>(Call.CS.getType());
11827a7e6055SDimitry Andric IRBuilder<> B(Call.CS.getInstruction());
11832cab237bSDimitry Andric Value *Addr =
11842cab237bSDimitry Andric B.CreateGEP(Int8Ty, B.CreateBitCast(Call.VTable, Int8PtrTy), Byte);
11857a7e6055SDimitry Andric if (RetType->getBitWidth() == 1) {
11867a7e6055SDimitry Andric Value *Bits = B.CreateLoad(Addr);
11877a7e6055SDimitry Andric Value *BitsAndBit = B.CreateAnd(Bits, Bit);
11887a7e6055SDimitry Andric auto IsBitSet = B.CreateICmpNE(BitsAndBit, ConstantInt::get(Int8Ty, 0));
11897a7e6055SDimitry Andric Call.replaceAndErase("virtual-const-prop-1-bit", FnName, RemarksEnabled,
11902cab237bSDimitry Andric OREGetter, IsBitSet);
11917a7e6055SDimitry Andric } else {
11927a7e6055SDimitry Andric Value *ValAddr = B.CreateBitCast(Addr, RetType->getPointerTo());
11937a7e6055SDimitry Andric Value *Val = B.CreateLoad(RetType, ValAddr);
11942cab237bSDimitry Andric Call.replaceAndErase("virtual-const-prop", FnName, RemarksEnabled,
11952cab237bSDimitry Andric OREGetter, Val);
11967a7e6055SDimitry Andric }
11977a7e6055SDimitry Andric }
11987a7e6055SDimitry Andric CSInfo.markDevirt();
11997a7e6055SDimitry Andric }
12007a7e6055SDimitry Andric
tryVirtualConstProp(MutableArrayRef<VirtualCallTarget> TargetsForSlot,VTableSlotInfo & SlotInfo,WholeProgramDevirtResolution * Res,VTableSlot Slot)12013ca95b02SDimitry Andric bool DevirtModule::tryVirtualConstProp(
12027a7e6055SDimitry Andric MutableArrayRef<VirtualCallTarget> TargetsForSlot, VTableSlotInfo &SlotInfo,
12037a7e6055SDimitry Andric WholeProgramDevirtResolution *Res, VTableSlot Slot) {
12043ca95b02SDimitry Andric // This only works if the function returns an integer.
12053ca95b02SDimitry Andric auto RetType = dyn_cast<IntegerType>(TargetsForSlot[0].Fn->getReturnType());
12063ca95b02SDimitry Andric if (!RetType)
12073ca95b02SDimitry Andric return false;
12083ca95b02SDimitry Andric unsigned BitWidth = RetType->getBitWidth();
12093ca95b02SDimitry Andric if (BitWidth > 64)
12103ca95b02SDimitry Andric return false;
12113ca95b02SDimitry Andric
12127a7e6055SDimitry Andric // Make sure that each function is defined, does not access memory, takes at
12137a7e6055SDimitry Andric // least one argument, does not use its first argument (which we assume is
12147a7e6055SDimitry Andric // 'this'), and has the same return type.
12157a7e6055SDimitry Andric //
12167a7e6055SDimitry Andric // Note that we test whether this copy of the function is readnone, rather
12177a7e6055SDimitry Andric // than testing function attributes, which must hold for any copy of the
12187a7e6055SDimitry Andric // function, even a less optimized version substituted at link time. This is
12197a7e6055SDimitry Andric // sound because the virtual constant propagation optimizations effectively
12207a7e6055SDimitry Andric // inline all implementations of the virtual function into each call site,
12217a7e6055SDimitry Andric // rather than using function attributes to perform local optimization.
12223ca95b02SDimitry Andric for (VirtualCallTarget &Target : TargetsForSlot) {
12237a7e6055SDimitry Andric if (Target.Fn->isDeclaration() ||
12247a7e6055SDimitry Andric computeFunctionBodyMemoryAccess(*Target.Fn, AARGetter(*Target.Fn)) !=
12257a7e6055SDimitry Andric MAK_ReadNone ||
12267a7e6055SDimitry Andric Target.Fn->arg_empty() || !Target.Fn->arg_begin()->use_empty() ||
12273ca95b02SDimitry Andric Target.Fn->getReturnType() != RetType)
12283ca95b02SDimitry Andric return false;
12293ca95b02SDimitry Andric }
12303ca95b02SDimitry Andric
12317a7e6055SDimitry Andric for (auto &&CSByConstantArg : SlotInfo.ConstCSInfo) {
12323ca95b02SDimitry Andric if (!tryEvaluateFunctionsWithArgs(TargetsForSlot, CSByConstantArg.first))
12333ca95b02SDimitry Andric continue;
12343ca95b02SDimitry Andric
12357a7e6055SDimitry Andric WholeProgramDevirtResolution::ByArg *ResByArg = nullptr;
12367a7e6055SDimitry Andric if (Res)
12377a7e6055SDimitry Andric ResByArg = &Res->ResByArg[CSByConstantArg.first];
12387a7e6055SDimitry Andric
12397a7e6055SDimitry Andric if (tryUniformRetValOpt(TargetsForSlot, CSByConstantArg.second, ResByArg))
12403ca95b02SDimitry Andric continue;
12413ca95b02SDimitry Andric
12427a7e6055SDimitry Andric if (tryUniqueRetValOpt(BitWidth, TargetsForSlot, CSByConstantArg.second,
12437a7e6055SDimitry Andric ResByArg, Slot, CSByConstantArg.first))
12443ca95b02SDimitry Andric continue;
12453ca95b02SDimitry Andric
12463ca95b02SDimitry Andric // Find an allocation offset in bits in all vtables associated with the
12473ca95b02SDimitry Andric // type.
12483ca95b02SDimitry Andric uint64_t AllocBefore =
12493ca95b02SDimitry Andric findLowestOffset(TargetsForSlot, /*IsAfter=*/false, BitWidth);
12503ca95b02SDimitry Andric uint64_t AllocAfter =
12513ca95b02SDimitry Andric findLowestOffset(TargetsForSlot, /*IsAfter=*/true, BitWidth);
12523ca95b02SDimitry Andric
12533ca95b02SDimitry Andric // Calculate the total amount of padding needed to store a value at both
12543ca95b02SDimitry Andric // ends of the object.
12553ca95b02SDimitry Andric uint64_t TotalPaddingBefore = 0, TotalPaddingAfter = 0;
12563ca95b02SDimitry Andric for (auto &&Target : TargetsForSlot) {
12573ca95b02SDimitry Andric TotalPaddingBefore += std::max<int64_t>(
12583ca95b02SDimitry Andric (AllocBefore + 7) / 8 - Target.allocatedBeforeBytes() - 1, 0);
12593ca95b02SDimitry Andric TotalPaddingAfter += std::max<int64_t>(
12603ca95b02SDimitry Andric (AllocAfter + 7) / 8 - Target.allocatedAfterBytes() - 1, 0);
12613ca95b02SDimitry Andric }
12623ca95b02SDimitry Andric
12633ca95b02SDimitry Andric // If the amount of padding is too large, give up.
12643ca95b02SDimitry Andric // FIXME: do something smarter here.
12653ca95b02SDimitry Andric if (std::min(TotalPaddingBefore, TotalPaddingAfter) > 128)
12663ca95b02SDimitry Andric continue;
12673ca95b02SDimitry Andric
12683ca95b02SDimitry Andric // Calculate the offset to the value as a (possibly negative) byte offset
12693ca95b02SDimitry Andric // and (if applicable) a bit offset, and store the values in the targets.
12703ca95b02SDimitry Andric int64_t OffsetByte;
12713ca95b02SDimitry Andric uint64_t OffsetBit;
12723ca95b02SDimitry Andric if (TotalPaddingBefore <= TotalPaddingAfter)
12733ca95b02SDimitry Andric setBeforeReturnValues(TargetsForSlot, AllocBefore, BitWidth, OffsetByte,
12743ca95b02SDimitry Andric OffsetBit);
12753ca95b02SDimitry Andric else
12763ca95b02SDimitry Andric setAfterReturnValues(TargetsForSlot, AllocAfter, BitWidth, OffsetByte,
12773ca95b02SDimitry Andric OffsetBit);
12783ca95b02SDimitry Andric
1279d88c1a5aSDimitry Andric if (RemarksEnabled)
1280d88c1a5aSDimitry Andric for (auto &&Target : TargetsForSlot)
1281d88c1a5aSDimitry Andric Target.WasDevirt = true;
1282d88c1a5aSDimitry Andric
12837a7e6055SDimitry Andric
12847a7e6055SDimitry Andric if (CSByConstantArg.second.isExported()) {
12857a7e6055SDimitry Andric ResByArg->TheKind = WholeProgramDevirtResolution::ByArg::VirtualConstProp;
12862cab237bSDimitry Andric exportConstant(Slot, CSByConstantArg.first, "byte", OffsetByte,
12872cab237bSDimitry Andric ResByArg->Byte);
12882cab237bSDimitry Andric exportConstant(Slot, CSByConstantArg.first, "bit", 1ULL << OffsetBit,
12892cab237bSDimitry Andric ResByArg->Bit);
12907a7e6055SDimitry Andric }
12917a7e6055SDimitry Andric
12923ca95b02SDimitry Andric // Rewrite each call to a load from OffsetByte/OffsetBit.
12932cab237bSDimitry Andric Constant *ByteConst = ConstantInt::get(Int32Ty, OffsetByte);
12942cab237bSDimitry Andric Constant *BitConst = ConstantInt::get(Int8Ty, 1ULL << OffsetBit);
12957a7e6055SDimitry Andric applyVirtualConstProp(CSByConstantArg.second,
12967a7e6055SDimitry Andric TargetsForSlot[0].Fn->getName(), ByteConst, BitConst);
12973ca95b02SDimitry Andric }
12983ca95b02SDimitry Andric return true;
12993ca95b02SDimitry Andric }
13003ca95b02SDimitry Andric
rebuildGlobal(VTableBits & B)13013ca95b02SDimitry Andric void DevirtModule::rebuildGlobal(VTableBits &B) {
13023ca95b02SDimitry Andric if (B.Before.Bytes.empty() && B.After.Bytes.empty())
13033ca95b02SDimitry Andric return;
13043ca95b02SDimitry Andric
13053ca95b02SDimitry Andric // Align each byte array to pointer width.
13063ca95b02SDimitry Andric unsigned PointerSize = M.getDataLayout().getPointerSize();
13073ca95b02SDimitry Andric B.Before.Bytes.resize(alignTo(B.Before.Bytes.size(), PointerSize));
13083ca95b02SDimitry Andric B.After.Bytes.resize(alignTo(B.After.Bytes.size(), PointerSize));
13093ca95b02SDimitry Andric
13103ca95b02SDimitry Andric // Before was stored in reverse order; flip it now.
13113ca95b02SDimitry Andric for (size_t I = 0, Size = B.Before.Bytes.size(); I != Size / 2; ++I)
13123ca95b02SDimitry Andric std::swap(B.Before.Bytes[I], B.Before.Bytes[Size - 1 - I]);
13133ca95b02SDimitry Andric
13143ca95b02SDimitry Andric // Build an anonymous global containing the before bytes, followed by the
13153ca95b02SDimitry Andric // original initializer, followed by the after bytes.
13163ca95b02SDimitry Andric auto NewInit = ConstantStruct::getAnon(
13173ca95b02SDimitry Andric {ConstantDataArray::get(M.getContext(), B.Before.Bytes),
13183ca95b02SDimitry Andric B.GV->getInitializer(),
13193ca95b02SDimitry Andric ConstantDataArray::get(M.getContext(), B.After.Bytes)});
13203ca95b02SDimitry Andric auto NewGV =
13213ca95b02SDimitry Andric new GlobalVariable(M, NewInit->getType(), B.GV->isConstant(),
13223ca95b02SDimitry Andric GlobalVariable::PrivateLinkage, NewInit, "", B.GV);
13233ca95b02SDimitry Andric NewGV->setSection(B.GV->getSection());
13243ca95b02SDimitry Andric NewGV->setComdat(B.GV->getComdat());
13253ca95b02SDimitry Andric
13263ca95b02SDimitry Andric // Copy the original vtable's metadata to the anonymous global, adjusting
13273ca95b02SDimitry Andric // offsets as required.
13283ca95b02SDimitry Andric NewGV->copyMetadata(B.GV, B.Before.Bytes.size());
13293ca95b02SDimitry Andric
13303ca95b02SDimitry Andric // Build an alias named after the original global, pointing at the second
13313ca95b02SDimitry Andric // element (the original initializer).
13323ca95b02SDimitry Andric auto Alias = GlobalAlias::create(
13333ca95b02SDimitry Andric B.GV->getInitializer()->getType(), 0, B.GV->getLinkage(), "",
13343ca95b02SDimitry Andric ConstantExpr::getGetElementPtr(
13353ca95b02SDimitry Andric NewInit->getType(), NewGV,
13363ca95b02SDimitry Andric ArrayRef<Constant *>{ConstantInt::get(Int32Ty, 0),
13373ca95b02SDimitry Andric ConstantInt::get(Int32Ty, 1)}),
13383ca95b02SDimitry Andric &M);
13393ca95b02SDimitry Andric Alias->setVisibility(B.GV->getVisibility());
13403ca95b02SDimitry Andric Alias->takeName(B.GV);
13413ca95b02SDimitry Andric
13423ca95b02SDimitry Andric B.GV->replaceAllUsesWith(Alias);
13433ca95b02SDimitry Andric B.GV->eraseFromParent();
13443ca95b02SDimitry Andric }
13453ca95b02SDimitry Andric
areRemarksEnabled()1346d88c1a5aSDimitry Andric bool DevirtModule::areRemarksEnabled() {
1347d88c1a5aSDimitry Andric const auto &FL = M.getFunctionList();
1348*b5893f02SDimitry Andric for (const Function &Fn : FL) {
13497a7e6055SDimitry Andric const auto &BBL = Fn.getBasicBlockList();
13507a7e6055SDimitry Andric if (BBL.empty())
1351*b5893f02SDimitry Andric continue;
13527a7e6055SDimitry Andric auto DI = OptimizationRemark(DEBUG_TYPE, "", DebugLoc(), &BBL.front());
1353d88c1a5aSDimitry Andric return DI.isEnabled();
1354d88c1a5aSDimitry Andric }
1355*b5893f02SDimitry Andric return false;
1356*b5893f02SDimitry Andric }
1357d88c1a5aSDimitry Andric
scanTypeTestUsers(Function * TypeTestFunc,Function * AssumeFunc)13583ca95b02SDimitry Andric void DevirtModule::scanTypeTestUsers(Function *TypeTestFunc,
13593ca95b02SDimitry Andric Function *AssumeFunc) {
13603ca95b02SDimitry Andric // Find all virtual calls via a virtual table pointer %p under an assumption
13613ca95b02SDimitry Andric // of the form llvm.assume(llvm.type.test(%p, %md)). This indicates that %p
13623ca95b02SDimitry Andric // points to a member of the type identifier %md. Group calls by (type ID,
13633ca95b02SDimitry Andric // offset) pair (effectively the identity of the virtual function) and store
13643ca95b02SDimitry Andric // to CallSlots.
1365*b5893f02SDimitry Andric DenseSet<CallSite> SeenCallSites;
13663ca95b02SDimitry Andric for (auto I = TypeTestFunc->use_begin(), E = TypeTestFunc->use_end();
13673ca95b02SDimitry Andric I != E;) {
13683ca95b02SDimitry Andric auto CI = dyn_cast<CallInst>(I->getUser());
13693ca95b02SDimitry Andric ++I;
13703ca95b02SDimitry Andric if (!CI)
13713ca95b02SDimitry Andric continue;
13723ca95b02SDimitry Andric
13733ca95b02SDimitry Andric // Search for virtual calls based on %p and add them to DevirtCalls.
13743ca95b02SDimitry Andric SmallVector<DevirtCallSite, 1> DevirtCalls;
13753ca95b02SDimitry Andric SmallVector<CallInst *, 1> Assumes;
1376*b5893f02SDimitry Andric auto &DT = LookupDomTree(*CI->getFunction());
1377*b5893f02SDimitry Andric findDevirtualizableCallsForTypeTest(DevirtCalls, Assumes, CI, DT);
13783ca95b02SDimitry Andric
1379*b5893f02SDimitry Andric // If we found any, add them to CallSlots.
13803ca95b02SDimitry Andric if (!Assumes.empty()) {
13813ca95b02SDimitry Andric Metadata *TypeId =
13823ca95b02SDimitry Andric cast<MetadataAsValue>(CI->getArgOperand(1))->getMetadata();
13833ca95b02SDimitry Andric Value *Ptr = CI->getArgOperand(0)->stripPointerCasts();
13843ca95b02SDimitry Andric for (DevirtCallSite Call : DevirtCalls) {
1385*b5893f02SDimitry Andric // Only add this CallSite if we haven't seen it before. The vtable
1386*b5893f02SDimitry Andric // pointer may have been CSE'd with pointers from other call sites,
1387*b5893f02SDimitry Andric // and we don't want to process call sites multiple times. We can't
1388*b5893f02SDimitry Andric // just skip the vtable Ptr if it has been seen before, however, since
1389*b5893f02SDimitry Andric // it may be shared by type tests that dominate different calls.
1390*b5893f02SDimitry Andric if (SeenCallSites.insert(Call.CS).second)
13912cab237bSDimitry Andric CallSlots[{TypeId, Call.Offset}].addCallSite(Ptr, Call.CS, nullptr);
13923ca95b02SDimitry Andric }
13933ca95b02SDimitry Andric }
13943ca95b02SDimitry Andric
13953ca95b02SDimitry Andric // We no longer need the assumes or the type test.
13963ca95b02SDimitry Andric for (auto Assume : Assumes)
13973ca95b02SDimitry Andric Assume->eraseFromParent();
13983ca95b02SDimitry Andric // We can't use RecursivelyDeleteTriviallyDeadInstructions here because we
13993ca95b02SDimitry Andric // may use the vtable argument later.
14003ca95b02SDimitry Andric if (CI->use_empty())
14013ca95b02SDimitry Andric CI->eraseFromParent();
14023ca95b02SDimitry Andric }
14033ca95b02SDimitry Andric }
14043ca95b02SDimitry Andric
scanTypeCheckedLoadUsers(Function * TypeCheckedLoadFunc)14053ca95b02SDimitry Andric void DevirtModule::scanTypeCheckedLoadUsers(Function *TypeCheckedLoadFunc) {
14063ca95b02SDimitry Andric Function *TypeTestFunc = Intrinsic::getDeclaration(&M, Intrinsic::type_test);
14073ca95b02SDimitry Andric
14083ca95b02SDimitry Andric for (auto I = TypeCheckedLoadFunc->use_begin(),
14093ca95b02SDimitry Andric E = TypeCheckedLoadFunc->use_end();
14103ca95b02SDimitry Andric I != E;) {
14113ca95b02SDimitry Andric auto CI = dyn_cast<CallInst>(I->getUser());
14123ca95b02SDimitry Andric ++I;
14133ca95b02SDimitry Andric if (!CI)
14143ca95b02SDimitry Andric continue;
14153ca95b02SDimitry Andric
14163ca95b02SDimitry Andric Value *Ptr = CI->getArgOperand(0);
14173ca95b02SDimitry Andric Value *Offset = CI->getArgOperand(1);
14183ca95b02SDimitry Andric Value *TypeIdValue = CI->getArgOperand(2);
14193ca95b02SDimitry Andric Metadata *TypeId = cast<MetadataAsValue>(TypeIdValue)->getMetadata();
14203ca95b02SDimitry Andric
14213ca95b02SDimitry Andric SmallVector<DevirtCallSite, 1> DevirtCalls;
14223ca95b02SDimitry Andric SmallVector<Instruction *, 1> LoadedPtrs;
14233ca95b02SDimitry Andric SmallVector<Instruction *, 1> Preds;
14243ca95b02SDimitry Andric bool HasNonCallUses = false;
1425*b5893f02SDimitry Andric auto &DT = LookupDomTree(*CI->getFunction());
14263ca95b02SDimitry Andric findDevirtualizableCallsForTypeCheckedLoad(DevirtCalls, LoadedPtrs, Preds,
1427*b5893f02SDimitry Andric HasNonCallUses, CI, DT);
14283ca95b02SDimitry Andric
14293ca95b02SDimitry Andric // Start by generating "pessimistic" code that explicitly loads the function
14303ca95b02SDimitry Andric // pointer from the vtable and performs the type check. If possible, we will
14313ca95b02SDimitry Andric // eliminate the load and the type check later.
14323ca95b02SDimitry Andric
14333ca95b02SDimitry Andric // If possible, only generate the load at the point where it is used.
14343ca95b02SDimitry Andric // This helps avoid unnecessary spills.
14353ca95b02SDimitry Andric IRBuilder<> LoadB(
14363ca95b02SDimitry Andric (LoadedPtrs.size() == 1 && !HasNonCallUses) ? LoadedPtrs[0] : CI);
14373ca95b02SDimitry Andric Value *GEP = LoadB.CreateGEP(Int8Ty, Ptr, Offset);
14383ca95b02SDimitry Andric Value *GEPPtr = LoadB.CreateBitCast(GEP, PointerType::getUnqual(Int8PtrTy));
14393ca95b02SDimitry Andric Value *LoadedValue = LoadB.CreateLoad(Int8PtrTy, GEPPtr);
14403ca95b02SDimitry Andric
14413ca95b02SDimitry Andric for (Instruction *LoadedPtr : LoadedPtrs) {
14423ca95b02SDimitry Andric LoadedPtr->replaceAllUsesWith(LoadedValue);
14433ca95b02SDimitry Andric LoadedPtr->eraseFromParent();
14443ca95b02SDimitry Andric }
14453ca95b02SDimitry Andric
14463ca95b02SDimitry Andric // Likewise for the type test.
14473ca95b02SDimitry Andric IRBuilder<> CallB((Preds.size() == 1 && !HasNonCallUses) ? Preds[0] : CI);
14483ca95b02SDimitry Andric CallInst *TypeTestCall = CallB.CreateCall(TypeTestFunc, {Ptr, TypeIdValue});
14493ca95b02SDimitry Andric
14503ca95b02SDimitry Andric for (Instruction *Pred : Preds) {
14513ca95b02SDimitry Andric Pred->replaceAllUsesWith(TypeTestCall);
14523ca95b02SDimitry Andric Pred->eraseFromParent();
14533ca95b02SDimitry Andric }
14543ca95b02SDimitry Andric
14553ca95b02SDimitry Andric // We have already erased any extractvalue instructions that refer to the
14563ca95b02SDimitry Andric // intrinsic call, but the intrinsic may have other non-extractvalue uses
14573ca95b02SDimitry Andric // (although this is unlikely). In that case, explicitly build a pair and
14583ca95b02SDimitry Andric // RAUW it.
14593ca95b02SDimitry Andric if (!CI->use_empty()) {
14603ca95b02SDimitry Andric Value *Pair = UndefValue::get(CI->getType());
14613ca95b02SDimitry Andric IRBuilder<> B(CI);
14623ca95b02SDimitry Andric Pair = B.CreateInsertValue(Pair, LoadedValue, {0});
14633ca95b02SDimitry Andric Pair = B.CreateInsertValue(Pair, TypeTestCall, {1});
14643ca95b02SDimitry Andric CI->replaceAllUsesWith(Pair);
14653ca95b02SDimitry Andric }
14663ca95b02SDimitry Andric
14673ca95b02SDimitry Andric // The number of unsafe uses is initially the number of uses.
14683ca95b02SDimitry Andric auto &NumUnsafeUses = NumUnsafeUsesForTypeTest[TypeTestCall];
14693ca95b02SDimitry Andric NumUnsafeUses = DevirtCalls.size();
14703ca95b02SDimitry Andric
14713ca95b02SDimitry Andric // If the function pointer has a non-call user, we cannot eliminate the type
14723ca95b02SDimitry Andric // check, as one of those users may eventually call the pointer. Increment
14733ca95b02SDimitry Andric // the unsafe use count to make sure it cannot reach zero.
14743ca95b02SDimitry Andric if (HasNonCallUses)
14753ca95b02SDimitry Andric ++NumUnsafeUses;
14763ca95b02SDimitry Andric for (DevirtCallSite Call : DevirtCalls) {
14777a7e6055SDimitry Andric CallSlots[{TypeId, Call.Offset}].addCallSite(Ptr, Call.CS,
14787a7e6055SDimitry Andric &NumUnsafeUses);
14793ca95b02SDimitry Andric }
14803ca95b02SDimitry Andric
14813ca95b02SDimitry Andric CI->eraseFromParent();
14823ca95b02SDimitry Andric }
14833ca95b02SDimitry Andric }
14843ca95b02SDimitry Andric
importResolution(VTableSlot Slot,VTableSlotInfo & SlotInfo)14857a7e6055SDimitry Andric void DevirtModule::importResolution(VTableSlot Slot, VTableSlotInfo &SlotInfo) {
14867a7e6055SDimitry Andric const TypeIdSummary *TidSummary =
14877a7e6055SDimitry Andric ImportSummary->getTypeIdSummary(cast<MDString>(Slot.TypeID)->getString());
14887a7e6055SDimitry Andric if (!TidSummary)
14897a7e6055SDimitry Andric return;
14907a7e6055SDimitry Andric auto ResI = TidSummary->WPDRes.find(Slot.ByteOffset);
14917a7e6055SDimitry Andric if (ResI == TidSummary->WPDRes.end())
14927a7e6055SDimitry Andric return;
14937a7e6055SDimitry Andric const WholeProgramDevirtResolution &Res = ResI->second;
14947a7e6055SDimitry Andric
14957a7e6055SDimitry Andric if (Res.TheKind == WholeProgramDevirtResolution::SingleImpl) {
14967a7e6055SDimitry Andric // The type of the function in the declaration is irrelevant because every
14977a7e6055SDimitry Andric // call site will cast it to the correct type.
14987a7e6055SDimitry Andric auto *SingleImpl = M.getOrInsertFunction(
14997a7e6055SDimitry Andric Res.SingleImplName, Type::getVoidTy(M.getContext()));
15007a7e6055SDimitry Andric
15017a7e6055SDimitry Andric // This is the import phase so we should not be exporting anything.
15027a7e6055SDimitry Andric bool IsExported = false;
15037a7e6055SDimitry Andric applySingleImplDevirt(SlotInfo, SingleImpl, IsExported);
15047a7e6055SDimitry Andric assert(!IsExported);
15057a7e6055SDimitry Andric }
15067a7e6055SDimitry Andric
15077a7e6055SDimitry Andric for (auto &CSByConstantArg : SlotInfo.ConstCSInfo) {
15087a7e6055SDimitry Andric auto I = Res.ResByArg.find(CSByConstantArg.first);
15097a7e6055SDimitry Andric if (I == Res.ResByArg.end())
15107a7e6055SDimitry Andric continue;
15117a7e6055SDimitry Andric auto &ResByArg = I->second;
15127a7e6055SDimitry Andric // FIXME: We should figure out what to do about the "function name" argument
15137a7e6055SDimitry Andric // to the apply* functions, as the function names are unavailable during the
15147a7e6055SDimitry Andric // importing phase. For now we just pass the empty string. This does not
15157a7e6055SDimitry Andric // impact correctness because the function names are just used for remarks.
15167a7e6055SDimitry Andric switch (ResByArg.TheKind) {
15177a7e6055SDimitry Andric case WholeProgramDevirtResolution::ByArg::UniformRetVal:
15187a7e6055SDimitry Andric applyUniformRetValOpt(CSByConstantArg.second, "", ResByArg.Info);
15197a7e6055SDimitry Andric break;
15207a7e6055SDimitry Andric case WholeProgramDevirtResolution::ByArg::UniqueRetVal: {
15217a7e6055SDimitry Andric Constant *UniqueMemberAddr =
15227a7e6055SDimitry Andric importGlobal(Slot, CSByConstantArg.first, "unique_member");
15237a7e6055SDimitry Andric applyUniqueRetValOpt(CSByConstantArg.second, "", ResByArg.Info,
15247a7e6055SDimitry Andric UniqueMemberAddr);
15257a7e6055SDimitry Andric break;
15267a7e6055SDimitry Andric }
15277a7e6055SDimitry Andric case WholeProgramDevirtResolution::ByArg::VirtualConstProp: {
15282cab237bSDimitry Andric Constant *Byte = importConstant(Slot, CSByConstantArg.first, "byte",
15292cab237bSDimitry Andric Int32Ty, ResByArg.Byte);
15302cab237bSDimitry Andric Constant *Bit = importConstant(Slot, CSByConstantArg.first, "bit", Int8Ty,
15312cab237bSDimitry Andric ResByArg.Bit);
15327a7e6055SDimitry Andric applyVirtualConstProp(CSByConstantArg.second, "", Byte, Bit);
1533da09e106SDimitry Andric break;
15347a7e6055SDimitry Andric }
15357a7e6055SDimitry Andric default:
15367a7e6055SDimitry Andric break;
15377a7e6055SDimitry Andric }
15387a7e6055SDimitry Andric }
15394ba319b5SDimitry Andric
15404ba319b5SDimitry Andric if (Res.TheKind == WholeProgramDevirtResolution::BranchFunnel) {
15414ba319b5SDimitry Andric auto *JT = M.getOrInsertFunction(getGlobalName(Slot, {}, "branch_funnel"),
15424ba319b5SDimitry Andric Type::getVoidTy(M.getContext()));
15434ba319b5SDimitry Andric bool IsExported = false;
15444ba319b5SDimitry Andric applyICallBranchFunnel(SlotInfo, JT, IsExported);
15454ba319b5SDimitry Andric assert(!IsExported);
15464ba319b5SDimitry Andric }
15477a7e6055SDimitry Andric }
15487a7e6055SDimitry Andric
removeRedundantTypeTests()15497a7e6055SDimitry Andric void DevirtModule::removeRedundantTypeTests() {
15507a7e6055SDimitry Andric auto True = ConstantInt::getTrue(M.getContext());
15517a7e6055SDimitry Andric for (auto &&U : NumUnsafeUsesForTypeTest) {
15527a7e6055SDimitry Andric if (U.second == 0) {
15537a7e6055SDimitry Andric U.first->replaceAllUsesWith(True);
15547a7e6055SDimitry Andric U.first->eraseFromParent();
15557a7e6055SDimitry Andric }
15567a7e6055SDimitry Andric }
15577a7e6055SDimitry Andric }
15587a7e6055SDimitry Andric
run()15593ca95b02SDimitry Andric bool DevirtModule::run() {
15603ca95b02SDimitry Andric Function *TypeTestFunc =
15613ca95b02SDimitry Andric M.getFunction(Intrinsic::getName(Intrinsic::type_test));
15623ca95b02SDimitry Andric Function *TypeCheckedLoadFunc =
15633ca95b02SDimitry Andric M.getFunction(Intrinsic::getName(Intrinsic::type_checked_load));
15643ca95b02SDimitry Andric Function *AssumeFunc = M.getFunction(Intrinsic::getName(Intrinsic::assume));
15653ca95b02SDimitry Andric
1566*b5893f02SDimitry Andric // If only some of the modules were split, we cannot correctly handle
1567*b5893f02SDimitry Andric // code that contains type tests or type checked loads.
1568*b5893f02SDimitry Andric if ((ExportSummary && ExportSummary->partiallySplitLTOUnits()) ||
1569*b5893f02SDimitry Andric (ImportSummary && ImportSummary->partiallySplitLTOUnits())) {
1570*b5893f02SDimitry Andric if ((TypeTestFunc && !TypeTestFunc->use_empty()) ||
1571*b5893f02SDimitry Andric (TypeCheckedLoadFunc && !TypeCheckedLoadFunc->use_empty()))
1572*b5893f02SDimitry Andric report_fatal_error("inconsistent LTO Unit splitting with llvm.type.test "
1573*b5893f02SDimitry Andric "or llvm.type.checked.load");
1574*b5893f02SDimitry Andric return false;
1575*b5893f02SDimitry Andric }
1576*b5893f02SDimitry Andric
15777a7e6055SDimitry Andric // Normally if there are no users of the devirtualization intrinsics in the
15787a7e6055SDimitry Andric // module, this pass has nothing to do. But if we are exporting, we also need
15797a7e6055SDimitry Andric // to handle any users that appear only in the function summaries.
15807a7e6055SDimitry Andric if (!ExportSummary &&
15817a7e6055SDimitry Andric (!TypeTestFunc || TypeTestFunc->use_empty() || !AssumeFunc ||
15823ca95b02SDimitry Andric AssumeFunc->use_empty()) &&
15833ca95b02SDimitry Andric (!TypeCheckedLoadFunc || TypeCheckedLoadFunc->use_empty()))
15843ca95b02SDimitry Andric return false;
15853ca95b02SDimitry Andric
15863ca95b02SDimitry Andric if (TypeTestFunc && AssumeFunc)
15873ca95b02SDimitry Andric scanTypeTestUsers(TypeTestFunc, AssumeFunc);
15883ca95b02SDimitry Andric
15893ca95b02SDimitry Andric if (TypeCheckedLoadFunc)
15903ca95b02SDimitry Andric scanTypeCheckedLoadUsers(TypeCheckedLoadFunc);
15913ca95b02SDimitry Andric
15927a7e6055SDimitry Andric if (ImportSummary) {
15937a7e6055SDimitry Andric for (auto &S : CallSlots)
15947a7e6055SDimitry Andric importResolution(S.first, S.second);
15957a7e6055SDimitry Andric
15967a7e6055SDimitry Andric removeRedundantTypeTests();
15977a7e6055SDimitry Andric
15987a7e6055SDimitry Andric // The rest of the code is only necessary when exporting or during regular
15997a7e6055SDimitry Andric // LTO, so we are done.
16007a7e6055SDimitry Andric return true;
16017a7e6055SDimitry Andric }
16027a7e6055SDimitry Andric
16033ca95b02SDimitry Andric // Rebuild type metadata into a map for easy lookup.
16043ca95b02SDimitry Andric std::vector<VTableBits> Bits;
16053ca95b02SDimitry Andric DenseMap<Metadata *, std::set<TypeMemberInfo>> TypeIdMap;
16063ca95b02SDimitry Andric buildTypeIdentifierMap(Bits, TypeIdMap);
16073ca95b02SDimitry Andric if (TypeIdMap.empty())
16083ca95b02SDimitry Andric return true;
16093ca95b02SDimitry Andric
16107a7e6055SDimitry Andric // Collect information from summary about which calls to try to devirtualize.
16117a7e6055SDimitry Andric if (ExportSummary) {
16127a7e6055SDimitry Andric DenseMap<GlobalValue::GUID, TinyPtrVector<Metadata *>> MetadataByGUID;
16137a7e6055SDimitry Andric for (auto &P : TypeIdMap) {
16147a7e6055SDimitry Andric if (auto *TypeId = dyn_cast<MDString>(P.first))
16157a7e6055SDimitry Andric MetadataByGUID[GlobalValue::getGUID(TypeId->getString())].push_back(
16167a7e6055SDimitry Andric TypeId);
16177a7e6055SDimitry Andric }
16187a7e6055SDimitry Andric
16197a7e6055SDimitry Andric for (auto &P : *ExportSummary) {
16200f5676f4SDimitry Andric for (auto &S : P.second.SummaryList) {
16217a7e6055SDimitry Andric auto *FS = dyn_cast<FunctionSummary>(S.get());
16227a7e6055SDimitry Andric if (!FS)
16237a7e6055SDimitry Andric continue;
16247a7e6055SDimitry Andric // FIXME: Only add live functions.
16257a7e6055SDimitry Andric for (FunctionSummary::VFuncId VF : FS->type_test_assume_vcalls()) {
16267a7e6055SDimitry Andric for (Metadata *MD : MetadataByGUID[VF.GUID]) {
16274ba319b5SDimitry Andric CallSlots[{MD, VF.Offset}]
16284ba319b5SDimitry Andric .CSInfo.markSummaryHasTypeTestAssumeUsers();
16297a7e6055SDimitry Andric }
16307a7e6055SDimitry Andric }
16317a7e6055SDimitry Andric for (FunctionSummary::VFuncId VF : FS->type_checked_load_vcalls()) {
16327a7e6055SDimitry Andric for (Metadata *MD : MetadataByGUID[VF.GUID]) {
16334ba319b5SDimitry Andric CallSlots[{MD, VF.Offset}].CSInfo.addSummaryTypeCheckedLoadUser(FS);
16347a7e6055SDimitry Andric }
16357a7e6055SDimitry Andric }
16367a7e6055SDimitry Andric for (const FunctionSummary::ConstVCall &VC :
16377a7e6055SDimitry Andric FS->type_test_assume_const_vcalls()) {
16387a7e6055SDimitry Andric for (Metadata *MD : MetadataByGUID[VC.VFunc.GUID]) {
16397a7e6055SDimitry Andric CallSlots[{MD, VC.VFunc.Offset}]
16407a7e6055SDimitry Andric .ConstCSInfo[VC.Args]
16414ba319b5SDimitry Andric .markSummaryHasTypeTestAssumeUsers();
16427a7e6055SDimitry Andric }
16437a7e6055SDimitry Andric }
16447a7e6055SDimitry Andric for (const FunctionSummary::ConstVCall &VC :
16457a7e6055SDimitry Andric FS->type_checked_load_const_vcalls()) {
16467a7e6055SDimitry Andric for (Metadata *MD : MetadataByGUID[VC.VFunc.GUID]) {
16477a7e6055SDimitry Andric CallSlots[{MD, VC.VFunc.Offset}]
16487a7e6055SDimitry Andric .ConstCSInfo[VC.Args]
16494ba319b5SDimitry Andric .addSummaryTypeCheckedLoadUser(FS);
16507a7e6055SDimitry Andric }
16517a7e6055SDimitry Andric }
16527a7e6055SDimitry Andric }
16537a7e6055SDimitry Andric }
16547a7e6055SDimitry Andric }
16557a7e6055SDimitry Andric
16563ca95b02SDimitry Andric // For each (type, offset) pair:
16573ca95b02SDimitry Andric bool DidVirtualConstProp = false;
1658d88c1a5aSDimitry Andric std::map<std::string, Function*> DevirtTargets;
16593ca95b02SDimitry Andric for (auto &S : CallSlots) {
16603ca95b02SDimitry Andric // Search each of the members of the type identifier for the virtual
16613ca95b02SDimitry Andric // function implementation at offset S.first.ByteOffset, and add to
16623ca95b02SDimitry Andric // TargetsForSlot.
16633ca95b02SDimitry Andric std::vector<VirtualCallTarget> TargetsForSlot;
16647a7e6055SDimitry Andric if (tryFindVirtualCallTargets(TargetsForSlot, TypeIdMap[S.first.TypeID],
16657a7e6055SDimitry Andric S.first.ByteOffset)) {
16667a7e6055SDimitry Andric WholeProgramDevirtResolution *Res = nullptr;
16677a7e6055SDimitry Andric if (ExportSummary && isa<MDString>(S.first.TypeID))
16687a7e6055SDimitry Andric Res = &ExportSummary
16697a7e6055SDimitry Andric ->getOrInsertTypeIdSummary(
16707a7e6055SDimitry Andric cast<MDString>(S.first.TypeID)->getString())
16717a7e6055SDimitry Andric .WPDRes[S.first.ByteOffset];
16723ca95b02SDimitry Andric
16734ba319b5SDimitry Andric if (!trySingleImplDevirt(TargetsForSlot, S.second, Res)) {
16744ba319b5SDimitry Andric DidVirtualConstProp |=
16754ba319b5SDimitry Andric tryVirtualConstProp(TargetsForSlot, S.second, Res, S.first);
16764ba319b5SDimitry Andric
16774ba319b5SDimitry Andric tryICallBranchFunnel(TargetsForSlot, S.second, Res, S.first);
16784ba319b5SDimitry Andric }
16793ca95b02SDimitry Andric
1680d88c1a5aSDimitry Andric // Collect functions devirtualized at least for one call site for stats.
1681d88c1a5aSDimitry Andric if (RemarksEnabled)
1682d88c1a5aSDimitry Andric for (const auto &T : TargetsForSlot)
1683d88c1a5aSDimitry Andric if (T.WasDevirt)
1684d88c1a5aSDimitry Andric DevirtTargets[T.Fn->getName()] = T.Fn;
1685d88c1a5aSDimitry Andric }
1686d88c1a5aSDimitry Andric
16877a7e6055SDimitry Andric // CFI-specific: if we are exporting and any llvm.type.checked.load
16887a7e6055SDimitry Andric // intrinsics were *not* devirtualized, we need to add the resulting
16897a7e6055SDimitry Andric // llvm.type.test intrinsics to the function summaries so that the
16907a7e6055SDimitry Andric // LowerTypeTests pass will export them.
16917a7e6055SDimitry Andric if (ExportSummary && isa<MDString>(S.first.TypeID)) {
16927a7e6055SDimitry Andric auto GUID =
16937a7e6055SDimitry Andric GlobalValue::getGUID(cast<MDString>(S.first.TypeID)->getString());
16947a7e6055SDimitry Andric for (auto FS : S.second.CSInfo.SummaryTypeCheckedLoadUsers)
16957a7e6055SDimitry Andric FS->addTypeTest(GUID);
16967a7e6055SDimitry Andric for (auto &CCS : S.second.ConstCSInfo)
16977a7e6055SDimitry Andric for (auto FS : CCS.second.SummaryTypeCheckedLoadUsers)
16987a7e6055SDimitry Andric FS->addTypeTest(GUID);
16997a7e6055SDimitry Andric }
17007a7e6055SDimitry Andric }
17017a7e6055SDimitry Andric
1702d88c1a5aSDimitry Andric if (RemarksEnabled) {
1703d88c1a5aSDimitry Andric // Generate remarks for each devirtualized function.
1704d88c1a5aSDimitry Andric for (const auto &DT : DevirtTargets) {
1705d88c1a5aSDimitry Andric Function *F = DT.second;
17062cab237bSDimitry Andric
17072cab237bSDimitry Andric using namespace ore;
17084ba319b5SDimitry Andric OREGetter(F).emit(OptimizationRemark(DEBUG_TYPE, "Devirtualized", F)
17094ba319b5SDimitry Andric << "devirtualized "
17104ba319b5SDimitry Andric << NV("FunctionName", F->getName()));
1711d88c1a5aSDimitry Andric }
17123ca95b02SDimitry Andric }
17133ca95b02SDimitry Andric
17147a7e6055SDimitry Andric removeRedundantTypeTests();
17153ca95b02SDimitry Andric
17163ca95b02SDimitry Andric // Rebuild each global we touched as part of virtual constant propagation to
17173ca95b02SDimitry Andric // include the before and after bytes.
17183ca95b02SDimitry Andric if (DidVirtualConstProp)
17193ca95b02SDimitry Andric for (VTableBits &B : Bits)
17203ca95b02SDimitry Andric rebuildGlobal(B);
17213ca95b02SDimitry Andric
17223ca95b02SDimitry Andric return true;
17233ca95b02SDimitry Andric }
1724