Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:44:41

0001 //===- WholeProgramDevirt.h - Whole-program devirt pass ---------*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 //
0009 // This file defines parts of the whole-program devirtualization pass
0010 // implementation that may be usefully unit tested.
0011 //
0012 //===----------------------------------------------------------------------===//
0013 
0014 #ifndef LLVM_TRANSFORMS_IPO_WHOLEPROGRAMDEVIRT_H
0015 #define LLVM_TRANSFORMS_IPO_WHOLEPROGRAMDEVIRT_H
0016 
0017 #include "llvm/ADT/DenseSet.h"
0018 #include "llvm/IR/GlobalValue.h"
0019 #include "llvm/IR/PassManager.h"
0020 #include <cassert>
0021 #include <cstdint>
0022 #include <map>
0023 #include <set>
0024 #include <utility>
0025 #include <vector>
0026 
0027 namespace llvm {
0028 class Module;
0029 
0030 template <typename T> class ArrayRef;
0031 template <typename T> class MutableArrayRef;
0032 class GlobalVariable;
0033 class ModuleSummaryIndex;
0034 struct ValueInfo;
0035 
0036 namespace wholeprogramdevirt {
0037 
0038 // A bit vector that keeps track of which bits are used. We use this to
0039 // pack constant values compactly before and after each virtual table.
0040 struct AccumBitVector {
0041   std::vector<uint8_t> Bytes;
0042 
0043   // Bits in BytesUsed[I] are 1 if matching bit in Bytes[I] is used, 0 if not.
0044   std::vector<uint8_t> BytesUsed;
0045 
0046   std::pair<uint8_t *, uint8_t *> getPtrToData(uint64_t Pos, uint8_t Size) {
0047     if (Bytes.size() < Pos + Size) {
0048       Bytes.resize(Pos + Size);
0049       BytesUsed.resize(Pos + Size);
0050     }
0051     return std::make_pair(Bytes.data() + Pos, BytesUsed.data() + Pos);
0052   }
0053 
0054   // Set little-endian value Val with size Size at bit position Pos,
0055   // and mark bytes as used.
0056   void setLE(uint64_t Pos, uint64_t Val, uint8_t Size) {
0057     assert(Pos % 8 == 0);
0058     auto DataUsed = getPtrToData(Pos / 8, Size);
0059     for (unsigned I = 0; I != Size; ++I) {
0060       DataUsed.first[I] = Val >> (I * 8);
0061       assert(!DataUsed.second[I]);
0062       DataUsed.second[I] = 0xff;
0063     }
0064   }
0065 
0066   // Set big-endian value Val with size Size at bit position Pos,
0067   // and mark bytes as used.
0068   void setBE(uint64_t Pos, uint64_t Val, uint8_t Size) {
0069     assert(Pos % 8 == 0);
0070     auto DataUsed = getPtrToData(Pos / 8, Size);
0071     for (unsigned I = 0; I != Size; ++I) {
0072       DataUsed.first[Size - I - 1] = Val >> (I * 8);
0073       assert(!DataUsed.second[Size - I - 1]);
0074       DataUsed.second[Size - I - 1] = 0xff;
0075     }
0076   }
0077 
0078   // Set bit at bit position Pos to b and mark bit as used.
0079   void setBit(uint64_t Pos, bool b) {
0080     auto DataUsed = getPtrToData(Pos / 8, 1);
0081     if (b)
0082       *DataUsed.first |= 1 << (Pos % 8);
0083     assert(!(*DataUsed.second & (1 << Pos % 8)));
0084     *DataUsed.second |= 1 << (Pos % 8);
0085   }
0086 };
0087 
0088 // The bits that will be stored before and after a particular vtable.
0089 struct VTableBits {
0090   // The vtable global.
0091   GlobalVariable *GV;
0092 
0093   // Cache of the vtable's size in bytes.
0094   uint64_t ObjectSize = 0;
0095 
0096   // The bit vector that will be laid out before the vtable. Note that these
0097   // bytes are stored in reverse order until the globals are rebuilt. This means
0098   // that any values in the array must be stored using the opposite endianness
0099   // from the target.
0100   AccumBitVector Before;
0101 
0102   // The bit vector that will be laid out after the vtable.
0103   AccumBitVector After;
0104 };
0105 
0106 // Information about a member of a particular type identifier.
0107 struct TypeMemberInfo {
0108   // The VTableBits for the vtable.
0109   VTableBits *Bits;
0110 
0111   // The offset in bytes from the start of the vtable (i.e. the address point).
0112   uint64_t Offset;
0113 
0114   bool operator<(const TypeMemberInfo &other) const {
0115     return Bits < other.Bits || (Bits == other.Bits && Offset < other.Offset);
0116   }
0117 };
0118 
0119 // A virtual call target, i.e. an entry in a particular vtable.
0120 struct VirtualCallTarget {
0121   VirtualCallTarget(GlobalValue *Fn, const TypeMemberInfo *TM);
0122 
0123   // For testing only.
0124   VirtualCallTarget(const TypeMemberInfo *TM, bool IsBigEndian)
0125       : Fn(nullptr), TM(TM), IsBigEndian(IsBigEndian), WasDevirt(false) {}
0126 
0127   // The function (or an alias to a function) stored in the vtable.
0128   GlobalValue *Fn;
0129 
0130   // A pointer to the type identifier member through which the pointer to Fn is
0131   // accessed.
0132   const TypeMemberInfo *TM;
0133 
0134   // When doing virtual constant propagation, this stores the return value for
0135   // the function when passed the currently considered argument list.
0136   uint64_t RetVal;
0137 
0138   // Whether the target is big endian.
0139   bool IsBigEndian;
0140 
0141   // Whether at least one call site to the target was devirtualized.
0142   bool WasDevirt;
0143 
0144   // The minimum byte offset before the address point. This covers the bytes in
0145   // the vtable object before the address point (e.g. RTTI, access-to-top,
0146   // vtables for other base classes) and is equal to the offset from the start
0147   // of the vtable object to the address point.
0148   uint64_t minBeforeBytes() const { return TM->Offset; }
0149 
0150   // The minimum byte offset after the address point. This covers the bytes in
0151   // the vtable object after the address point (e.g. the vtable for the current
0152   // class and any later base classes) and is equal to the size of the vtable
0153   // object minus the offset from the start of the vtable object to the address
0154   // point.
0155   uint64_t minAfterBytes() const { return TM->Bits->ObjectSize - TM->Offset; }
0156 
0157   // The number of bytes allocated (for the vtable plus the byte array) before
0158   // the address point.
0159   uint64_t allocatedBeforeBytes() const {
0160     return minBeforeBytes() + TM->Bits->Before.Bytes.size();
0161   }
0162 
0163   // The number of bytes allocated (for the vtable plus the byte array) after
0164   // the address point.
0165   uint64_t allocatedAfterBytes() const {
0166     return minAfterBytes() + TM->Bits->After.Bytes.size();
0167   }
0168 
0169   // Set the bit at position Pos before the address point to RetVal.
0170   void setBeforeBit(uint64_t Pos) {
0171     assert(Pos >= 8 * minBeforeBytes());
0172     TM->Bits->Before.setBit(Pos - 8 * minBeforeBytes(), RetVal);
0173   }
0174 
0175   // Set the bit at position Pos after the address point to RetVal.
0176   void setAfterBit(uint64_t Pos) {
0177     assert(Pos >= 8 * minAfterBytes());
0178     TM->Bits->After.setBit(Pos - 8 * minAfterBytes(), RetVal);
0179   }
0180 
0181   // Set the bytes at position Pos before the address point to RetVal.
0182   // Because the bytes in Before are stored in reverse order, we use the
0183   // opposite endianness to the target.
0184   void setBeforeBytes(uint64_t Pos, uint8_t Size) {
0185     assert(Pos >= 8 * minBeforeBytes());
0186     if (IsBigEndian)
0187       TM->Bits->Before.setLE(Pos - 8 * minBeforeBytes(), RetVal, Size);
0188     else
0189       TM->Bits->Before.setBE(Pos - 8 * minBeforeBytes(), RetVal, Size);
0190   }
0191 
0192   // Set the bytes at position Pos after the address point to RetVal.
0193   void setAfterBytes(uint64_t Pos, uint8_t Size) {
0194     assert(Pos >= 8 * minAfterBytes());
0195     if (IsBigEndian)
0196       TM->Bits->After.setBE(Pos - 8 * minAfterBytes(), RetVal, Size);
0197     else
0198       TM->Bits->After.setLE(Pos - 8 * minAfterBytes(), RetVal, Size);
0199   }
0200 };
0201 
0202 // Find the minimum offset that we may store a value of size Size bits at. If
0203 // IsAfter is set, look for an offset before the object, otherwise look for an
0204 // offset after the object.
0205 uint64_t findLowestOffset(ArrayRef<VirtualCallTarget> Targets, bool IsAfter,
0206                           uint64_t Size);
0207 
0208 // Set the stored value in each of Targets to VirtualCallTarget::RetVal at the
0209 // given allocation offset before the vtable address. Stores the computed
0210 // byte/bit offset to OffsetByte/OffsetBit.
0211 void setBeforeReturnValues(MutableArrayRef<VirtualCallTarget> Targets,
0212                            uint64_t AllocBefore, unsigned BitWidth,
0213                            int64_t &OffsetByte, uint64_t &OffsetBit);
0214 
0215 // Set the stored value in each of Targets to VirtualCallTarget::RetVal at the
0216 // given allocation offset after the vtable address. Stores the computed
0217 // byte/bit offset to OffsetByte/OffsetBit.
0218 void setAfterReturnValues(MutableArrayRef<VirtualCallTarget> Targets,
0219                           uint64_t AllocAfter, unsigned BitWidth,
0220                           int64_t &OffsetByte, uint64_t &OffsetBit);
0221 
0222 } // end namespace wholeprogramdevirt
0223 
0224 struct WholeProgramDevirtPass : public PassInfoMixin<WholeProgramDevirtPass> {
0225   ModuleSummaryIndex *ExportSummary;
0226   const ModuleSummaryIndex *ImportSummary;
0227   bool UseCommandLine = false;
0228   WholeProgramDevirtPass()
0229       : ExportSummary(nullptr), ImportSummary(nullptr), UseCommandLine(true) {}
0230   WholeProgramDevirtPass(ModuleSummaryIndex *ExportSummary,
0231                          const ModuleSummaryIndex *ImportSummary)
0232       : ExportSummary(ExportSummary), ImportSummary(ImportSummary) {
0233     assert(!(ExportSummary && ImportSummary));
0234   }
0235   PreservedAnalyses run(Module &M, ModuleAnalysisManager &);
0236 };
0237 
0238 struct VTableSlotSummary {
0239   StringRef TypeID;
0240   uint64_t ByteOffset;
0241 };
0242 bool hasWholeProgramVisibility(bool WholeProgramVisibilityEnabledInLTO);
0243 void updatePublicTypeTestCalls(Module &M,
0244                                bool WholeProgramVisibilityEnabledInLTO);
0245 void updateVCallVisibilityInModule(
0246     Module &M, bool WholeProgramVisibilityEnabledInLTO,
0247     const DenseSet<GlobalValue::GUID> &DynamicExportSymbols,
0248     bool ValidateAllVtablesHaveTypeInfos,
0249     function_ref<bool(StringRef)> IsVisibleToRegularObj);
0250 void updateVCallVisibilityInIndex(
0251     ModuleSummaryIndex &Index, bool WholeProgramVisibilityEnabledInLTO,
0252     const DenseSet<GlobalValue::GUID> &DynamicExportSymbols,
0253     const DenseSet<GlobalValue::GUID> &VisibleToRegularObjSymbols);
0254 
0255 void getVisibleToRegularObjVtableGUIDs(
0256     ModuleSummaryIndex &Index,
0257     DenseSet<GlobalValue::GUID> &VisibleToRegularObjSymbols,
0258     function_ref<bool(StringRef)> IsVisibleToRegularObj);
0259 
0260 /// Perform index-based whole program devirtualization on the \p Summary
0261 /// index. Any devirtualized targets used by a type test in another module
0262 /// are added to the \p ExportedGUIDs set. For any local devirtualized targets
0263 /// only used within the defining module, the information necessary for
0264 /// locating the corresponding WPD resolution is recorded for the ValueInfo
0265 /// in case it is exported by cross module importing (in which case the
0266 /// devirtualized target name will need adjustment).
0267 void runWholeProgramDevirtOnIndex(
0268     ModuleSummaryIndex &Summary, std::set<GlobalValue::GUID> &ExportedGUIDs,
0269     std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap);
0270 
0271 /// Call after cross-module importing to update the recorded single impl
0272 /// devirt target names for any locals that were exported.
0273 void updateIndexWPDForExports(
0274     ModuleSummaryIndex &Summary,
0275     function_ref<bool(StringRef, ValueInfo)> isExported,
0276     std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap);
0277 
0278 } // end namespace llvm
0279 
0280 #endif // LLVM_TRANSFORMS_IPO_WHOLEPROGRAMDEVIRT_H