Viewing file: WholeProgramDevirt.h (10.53 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
//===- WholeProgramDevirt.h - Whole-program devirt pass ---------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines parts of the whole-program devirtualization pass // implementation that may be usefully unit tested. // //===----------------------------------------------------------------------===//
#ifndef LLVM_TRANSFORMS_IPO_WHOLEPROGRAMDEVIRT_H #define LLVM_TRANSFORMS_IPO_WHOLEPROGRAMDEVIRT_H
#include "llvm/ADT/DenseSet.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/PassManager.h" #include <cassert> #include <cstdint> #include <map> #include <set> #include <utility> #include <vector>
namespace llvm { class Module;
template <typename T> class ArrayRef; template <typename T> class MutableArrayRef; class GlobalVariable; class ModuleSummaryIndex; struct ValueInfo;
namespace wholeprogramdevirt {
// A bit vector that keeps track of which bits are used. We use this to // pack constant values compactly before and after each virtual table. struct AccumBitVector { std::vector<uint8_t> Bytes;
// Bits in BytesUsed[I] are 1 if matching bit in Bytes[I] is used, 0 if not. std::vector<uint8_t> BytesUsed;
std::pair<uint8_t *, uint8_t *> getPtrToData(uint64_t Pos, uint8_t Size) { if (Bytes.size() < Pos + Size) { Bytes.resize(Pos + Size); BytesUsed.resize(Pos + Size); } return std::make_pair(Bytes.data() + Pos, BytesUsed.data() + Pos); }
// Set little-endian value Val with size Size at bit position Pos, // and mark bytes as used. void setLE(uint64_t Pos, uint64_t Val, uint8_t Size) { assert(Pos % 8 == 0); auto DataUsed = getPtrToData(Pos / 8, Size); for (unsigned I = 0; I != Size; ++I) { DataUsed.first[I] = Val >> (I * 8); assert(!DataUsed.second[I]); DataUsed.second[I] = 0xff; } }
// Set big-endian value Val with size Size at bit position Pos, // and mark bytes as used. void setBE(uint64_t Pos, uint64_t Val, uint8_t Size) { assert(Pos % 8 == 0); auto DataUsed = getPtrToData(Pos / 8, Size); for (unsigned I = 0; I != Size; ++I) { DataUsed.first[Size - I - 1] = Val >> (I * 8); assert(!DataUsed.second[Size - I - 1]); DataUsed.second[Size - I - 1] = 0xff; } }
// Set bit at bit position Pos to b and mark bit as used. void setBit(uint64_t Pos, bool b) { auto DataUsed = getPtrToData(Pos / 8, 1); if (b) *DataUsed.first |= 1 << (Pos % 8); assert(!(*DataUsed.second & (1 << Pos % 8))); *DataUsed.second |= 1 << (Pos % 8); } };
// The bits that will be stored before and after a particular vtable. struct VTableBits { // The vtable global. GlobalVariable *GV;
// Cache of the vtable's size in bytes. uint64_t ObjectSize = 0;
// The bit vector that will be laid out before the vtable. Note that these // bytes are stored in reverse order until the globals are rebuilt. This means // that any values in the array must be stored using the opposite endianness // from the target. AccumBitVector Before;
// The bit vector that will be laid out after the vtable. AccumBitVector After; };
// Information about a member of a particular type identifier. struct TypeMemberInfo { // The VTableBits for the vtable. VTableBits *Bits;
// The offset in bytes from the start of the vtable (i.e. the address point). uint64_t Offset;
bool operator<(const TypeMemberInfo &other) const { return Bits < other.Bits || (Bits == other.Bits && Offset < other.Offset); } };
// A virtual call target, i.e. an entry in a particular vtable. struct VirtualCallTarget { VirtualCallTarget(GlobalValue *Fn, const TypeMemberInfo *TM);
// For testing only. VirtualCallTarget(const TypeMemberInfo *TM, bool IsBigEndian) : Fn(nullptr), TM(TM), IsBigEndian(IsBigEndian), WasDevirt(false) {}
// The function (or an alias to a function) stored in the vtable. GlobalValue *Fn;
// A pointer to the type identifier member through which the pointer to Fn is // accessed. const TypeMemberInfo *TM;
// When doing virtual constant propagation, this stores the return value for // the function when passed the currently considered argument list. uint64_t RetVal;
// Whether the target is big endian. bool IsBigEndian;
// Whether at least one call site to the target was devirtualized. bool WasDevirt;
// The minimum byte offset before the address point. This covers the bytes in // the vtable object before the address point (e.g. RTTI, access-to-top, // vtables for other base classes) and is equal to the offset from the start // of the vtable object to the address point. uint64_t minBeforeBytes() const { return TM->Offset; }
// The minimum byte offset after the address point. This covers the bytes in // the vtable object after the address point (e.g. the vtable for the current // class and any later base classes) and is equal to the size of the vtable // object minus the offset from the start of the vtable object to the address // point. uint64_t minAfterBytes() const { return TM->Bits->ObjectSize - TM->Offset; }
// The number of bytes allocated (for the vtable plus the byte array) before // the address point. uint64_t allocatedBeforeBytes() const { return minBeforeBytes() + TM->Bits->Before.Bytes.size(); }
// The number of bytes allocated (for the vtable plus the byte array) after // the address point. uint64_t allocatedAfterBytes() const { return minAfterBytes() + TM->Bits->After.Bytes.size(); }
// Set the bit at position Pos before the address point to RetVal. void setBeforeBit(uint64_t Pos) { assert(Pos >= 8 * minBeforeBytes()); TM->Bits->Before.setBit(Pos - 8 * minBeforeBytes(), RetVal); }
// Set the bit at position Pos after the address point to RetVal. void setAfterBit(uint64_t Pos) { assert(Pos >= 8 * minAfterBytes()); TM->Bits->After.setBit(Pos - 8 * minAfterBytes(), RetVal); }
// Set the bytes at position Pos before the address point to RetVal. // Because the bytes in Before are stored in reverse order, we use the // opposite endianness to the target. void setBeforeBytes(uint64_t Pos, uint8_t Size) { assert(Pos >= 8 * minBeforeBytes()); if (IsBigEndian) TM->Bits->Before.setLE(Pos - 8 * minBeforeBytes(), RetVal, Size); else TM->Bits->Before.setBE(Pos - 8 * minBeforeBytes(), RetVal, Size); }
// Set the bytes at position Pos after the address point to RetVal. void setAfterBytes(uint64_t Pos, uint8_t Size) { assert(Pos >= 8 * minAfterBytes()); if (IsBigEndian) TM->Bits->After.setBE(Pos - 8 * minAfterBytes(), RetVal, Size); else TM->Bits->After.setLE(Pos - 8 * minAfterBytes(), RetVal, Size); } };
// Find the minimum offset that we may store a value of size Size bits at. If // IsAfter is set, look for an offset before the object, otherwise look for an // offset after the object. uint64_t findLowestOffset(ArrayRef<VirtualCallTarget> Targets, bool IsAfter, uint64_t Size);
// Set the stored value in each of Targets to VirtualCallTarget::RetVal at the // given allocation offset before the vtable address. Stores the computed // byte/bit offset to OffsetByte/OffsetBit. void setBeforeReturnValues(MutableArrayRef<VirtualCallTarget> Targets, uint64_t AllocBefore, unsigned BitWidth, int64_t &OffsetByte, uint64_t &OffsetBit);
// Set the stored value in each of Targets to VirtualCallTarget::RetVal at the // given allocation offset after the vtable address. Stores the computed // byte/bit offset to OffsetByte/OffsetBit. void setAfterReturnValues(MutableArrayRef<VirtualCallTarget> Targets, uint64_t AllocAfter, unsigned BitWidth, int64_t &OffsetByte, uint64_t &OffsetBit);
} // end namespace wholeprogramdevirt
struct WholeProgramDevirtPass : public PassInfoMixin<WholeProgramDevirtPass> { ModuleSummaryIndex *ExportSummary; const ModuleSummaryIndex *ImportSummary; bool UseCommandLine = false; WholeProgramDevirtPass() : ExportSummary(nullptr), ImportSummary(nullptr), UseCommandLine(true) {} WholeProgramDevirtPass(ModuleSummaryIndex *ExportSummary, const ModuleSummaryIndex *ImportSummary) : ExportSummary(ExportSummary), ImportSummary(ImportSummary) { assert(!(ExportSummary && ImportSummary)); } PreservedAnalyses run(Module &M, ModuleAnalysisManager &); };
struct VTableSlotSummary { StringRef TypeID; uint64_t ByteOffset; }; bool hasWholeProgramVisibility(bool WholeProgramVisibilityEnabledInLTO); void updatePublicTypeTestCalls(Module &M, bool WholeProgramVisibilityEnabledInLTO); void updateVCallVisibilityInModule( Module &M, bool WholeProgramVisibilityEnabledInLTO, const DenseSet<GlobalValue::GUID> &DynamicExportSymbols, bool ValidateAllVtablesHaveTypeInfos, function_ref<bool(StringRef)> IsVisibleToRegularObj); void updateVCallVisibilityInIndex( ModuleSummaryIndex &Index, bool WholeProgramVisibilityEnabledInLTO, const DenseSet<GlobalValue::GUID> &DynamicExportSymbols, const DenseSet<GlobalValue::GUID> &VisibleToRegularObjSymbols);
void getVisibleToRegularObjVtableGUIDs( ModuleSummaryIndex &Index, DenseSet<GlobalValue::GUID> &VisibleToRegularObjSymbols, function_ref<bool(StringRef)> IsVisibleToRegularObj);
/// Perform index-based whole program devirtualization on the \p Summary /// index. Any devirtualized targets used by a type test in another module /// are added to the \p ExportedGUIDs set. For any local devirtualized targets /// only used within the defining module, the information necessary for /// locating the corresponding WPD resolution is recorded for the ValueInfo /// in case it is exported by cross module importing (in which case the /// devirtualized target name will need adjustment). void runWholeProgramDevirtOnIndex( ModuleSummaryIndex &Summary, std::set<GlobalValue::GUID> &ExportedGUIDs, std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap);
/// Call after cross-module importing to update the recorded single impl /// devirt target names for any locals that were exported. void updateIndexWPDForExports( ModuleSummaryIndex &Summary, function_ref<bool(StringRef, ValueInfo)> isExported, std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap);
} // end namespace llvm
#endif // LLVM_TRANSFORMS_IPO_WHOLEPROGRAMDEVIRT_H
|