mirror of
https://github.com/intel/llvm.git
synced 2026-01-19 01:15:50 +08:00
[BOLT] Support multiple parents for split jump table
There are two assumptions regarding jump table: (a) It is accessed by only one fragment, say, Parent (b) All entries target instructions in Parent For (a), BOLT stores jump table entries as relative offset to Parent. For (b), BOLT treats jump table entries target somewhere out of Parent as INVALID_OFFSET, including fragment of same split function. In this update, we extend (a) and (b) to include fragment of same split functinon. For (a), we store jump table entries in absolute offset instead. In addition, jump table will store all fragments that access it. A fragment uses this information to only create label for jump table entries that target to that fragment. For (b), using absolute offset allows jump table entries to target fragments of same split function, i.e., extend support for split jump table. This can be done using relocation (fragment start/size) and fragment detection heuristics (e.g., using symbol name pattern for non-stripped binaries). For jump table targets that can only be reached by one fragment, we mark them as local label; otherwise, they would be the secondary function entry to the target fragment. Test Plan ``` ninja check-bolt ``` Reviewed By: Amir Differential Revision: https://reviews.llvm.org/D128474
This commit is contained in:
@@ -200,7 +200,7 @@ class BinaryContext {
|
||||
uint32_t DuplicatedJumpTables{0x10000000};
|
||||
|
||||
/// Function fragments to skip.
|
||||
std::vector<BinaryFunction *> FragmentsToSkip;
|
||||
std::unordered_set<BinaryFunction *> FragmentsToSkip;
|
||||
|
||||
/// The runtime library.
|
||||
std::unique_ptr<RuntimeLibrary> RtLibrary;
|
||||
@@ -236,6 +236,18 @@ public:
|
||||
MIB = std::move(TargetBuilder);
|
||||
}
|
||||
|
||||
/// Return function fragments to skip.
|
||||
const std::unordered_set<BinaryFunction *> &getFragmentsToSkip() {
|
||||
return FragmentsToSkip;
|
||||
}
|
||||
|
||||
/// Add function fragment to skip
|
||||
void addFragmentsToSkip(BinaryFunction *Function) {
|
||||
FragmentsToSkip.insert(Function);
|
||||
}
|
||||
|
||||
void clearFragmentsToSkip() { FragmentsToSkip.clear(); }
|
||||
|
||||
/// Given DWOId returns CU if it exists in DWOCUs.
|
||||
Optional<DWARFUnit *> getDWOCU(uint64_t DWOId);
|
||||
|
||||
@@ -476,15 +488,15 @@ public:
|
||||
/// If \p NextJTAddress is different from zero, it is used as an upper
|
||||
/// bound for jump table memory layout.
|
||||
///
|
||||
/// Optionally, populate \p Offsets with jump table entries. The entries
|
||||
/// Optionally, populate \p Address from jump table entries. The entries
|
||||
/// could be partially populated if the jump table detection fails.
|
||||
bool analyzeJumpTable(const uint64_t Address,
|
||||
const JumpTable::JumpTableType Type, BinaryFunction &BF,
|
||||
const uint64_t NextJTAddress = 0,
|
||||
JumpTable::OffsetsType *Offsets = nullptr);
|
||||
JumpTable::AddressesType *EntriesAsAddress = nullptr);
|
||||
|
||||
/// After jump table locations are established, this function will populate
|
||||
/// their OffsetEntries based on memory contents.
|
||||
/// their EntriesAsAddress based on memory contents.
|
||||
void populateJumpTables();
|
||||
|
||||
/// Returns a jump table ID and label pointing to the duplicated jump table.
|
||||
@@ -499,12 +511,12 @@ public:
|
||||
/// to function \p BF.
|
||||
std::string generateJumpTableName(const BinaryFunction &BF, uint64_t Address);
|
||||
|
||||
/// Free memory used by jump table offsets
|
||||
void clearJumpTableOffsets() {
|
||||
/// Free memory used by JumpTable's EntriesAsAddress
|
||||
void clearJumpTableTempData() {
|
||||
for (auto &JTI : JumpTables) {
|
||||
JumpTable &JT = *JTI.second;
|
||||
JumpTable::OffsetsType Temp;
|
||||
Temp.swap(JT.OffsetEntries);
|
||||
JumpTable::AddressesType Temp;
|
||||
Temp.swap(JT.EntriesAsAddress);
|
||||
}
|
||||
}
|
||||
/// Return true if the array of bytes represents a valid code padding.
|
||||
|
||||
@@ -333,9 +333,9 @@ private:
|
||||
/// True if the original entry point was patched.
|
||||
bool IsPatched{false};
|
||||
|
||||
/// True if the function contains jump table with entries pointing to
|
||||
/// locations in fragments.
|
||||
bool HasSplitJumpTable{false};
|
||||
/// True if the function contains explicit or implicit indirect branch to its
|
||||
/// split fragments, e.g., split jump table, landing pad in split fragment
|
||||
bool HasIndirectTargetToSplitFragment{false};
|
||||
|
||||
/// True if there are no control-flow edges with successors in other functions
|
||||
/// (i.e. if tail calls have edges to function-local basic blocks).
|
||||
@@ -1437,9 +1437,12 @@ public:
|
||||
/// otherwise processed.
|
||||
bool isPseudo() const { return IsPseudo; }
|
||||
|
||||
/// Return true if the function contains a jump table with entries pointing
|
||||
/// to split fragments.
|
||||
bool hasSplitJumpTable() const { return HasSplitJumpTable; }
|
||||
/// Return true if the function contains explicit or implicit indirect branch
|
||||
/// to its split fragments, e.g., split jump table, landing pad in split
|
||||
/// fragment.
|
||||
bool hasIndirectTargetToSplitFragment() const {
|
||||
return HasIndirectTargetToSplitFragment;
|
||||
}
|
||||
|
||||
/// Return true if all CFG edges have local successors.
|
||||
bool hasCanonicalCFG() const { return HasCanonicalCFG; }
|
||||
@@ -1834,7 +1837,9 @@ public:
|
||||
|
||||
void setIsPatched(bool V) { IsPatched = V; }
|
||||
|
||||
void setHasSplitJumpTable(bool V) { HasSplitJumpTable = V; }
|
||||
void setHasIndirectTargetToSplitFragment(bool V) {
|
||||
HasIndirectTargetToSplitFragment = V;
|
||||
}
|
||||
|
||||
void setHasCanonicalCFG(bool V) { HasCanonicalCFG = V; }
|
||||
|
||||
|
||||
@@ -69,9 +69,9 @@ public:
|
||||
/// All the entries as labels.
|
||||
std::vector<MCSymbol *> Entries;
|
||||
|
||||
/// All the entries as offsets into a function. Invalid after CFG is built.
|
||||
using OffsetsType = std::vector<uint64_t>;
|
||||
OffsetsType OffsetEntries;
|
||||
/// All the entries as absolute addresses. Invalid after disassembly is done.
|
||||
using AddressesType = std::vector<uint64_t>;
|
||||
AddressesType EntriesAsAddress;
|
||||
|
||||
/// Map <Offset> -> <Label> used for embedded jump tables. Label at 0 offset
|
||||
/// is the main label for the jump table.
|
||||
@@ -87,18 +87,17 @@ public:
|
||||
uint64_t Count{0};
|
||||
|
||||
/// BinaryFunction this jump tables belongs to.
|
||||
BinaryFunction *Parent{nullptr};
|
||||
SmallVector<BinaryFunction *, 1> Parents;
|
||||
|
||||
private:
|
||||
/// Constructor should only be called by a BinaryContext.
|
||||
JumpTable(MCSymbol &Symbol, uint64_t Address, size_t EntrySize,
|
||||
JumpTableType Type, LabelMapType &&Labels, BinaryFunction &BF,
|
||||
BinarySection &Section);
|
||||
JumpTableType Type, LabelMapType &&Labels, BinarySection &Section);
|
||||
|
||||
public:
|
||||
/// Return the size of the jump table.
|
||||
uint64_t getSize() const {
|
||||
return std::max(OffsetEntries.size(), Entries.size()) * EntrySize;
|
||||
return std::max(EntriesAsAddress.size(), Entries.size()) * EntrySize;
|
||||
}
|
||||
|
||||
const MCSymbol *getFirstLabel() const {
|
||||
|
||||
@@ -490,21 +490,19 @@ bool isPotentialFragmentByName(BinaryFunction &Fragment,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BinaryContext::analyzeJumpTable(const uint64_t Address,
|
||||
const JumpTable::JumpTableType Type,
|
||||
BinaryFunction &BF,
|
||||
const uint64_t NextJTAddress,
|
||||
JumpTable::OffsetsType *Offsets) {
|
||||
bool BinaryContext::analyzeJumpTable(
|
||||
const uint64_t Address, const JumpTable::JumpTableType Type,
|
||||
BinaryFunction &BF, const uint64_t NextJTAddress,
|
||||
JumpTable::AddressesType *EntriesAsAddress) {
|
||||
// Is one of the targets __builtin_unreachable?
|
||||
bool HasUnreachable = false;
|
||||
|
||||
// Number of targets other than __builtin_unreachable.
|
||||
uint64_t NumRealEntries = 0;
|
||||
|
||||
constexpr uint64_t INVALID_OFFSET = std::numeric_limits<uint64_t>::max();
|
||||
auto addOffset = [&](uint64_t Offset) {
|
||||
if (Offsets)
|
||||
Offsets->emplace_back(Offset);
|
||||
auto addEntryAddress = [&](uint64_t EntryAddress) {
|
||||
if (EntriesAsAddress)
|
||||
EntriesAsAddress->emplace_back(EntryAddress);
|
||||
};
|
||||
|
||||
auto doesBelongToFunction = [&](const uint64_t Addr,
|
||||
@@ -570,7 +568,7 @@ bool BinaryContext::analyzeJumpTable(const uint64_t Address,
|
||||
|
||||
// __builtin_unreachable() case.
|
||||
if (Value == BF.getAddress() + BF.getSize()) {
|
||||
addOffset(Value - BF.getAddress());
|
||||
addEntryAddress(Value);
|
||||
HasUnreachable = true;
|
||||
LLVM_DEBUG(dbgs() << "OK: __builtin_unreachable\n");
|
||||
continue;
|
||||
@@ -610,18 +608,9 @@ bool BinaryContext::analyzeJumpTable(const uint64_t Address,
|
||||
|
||||
++NumRealEntries;
|
||||
|
||||
if (TargetBF == &BF) {
|
||||
// Address inside the function.
|
||||
addOffset(Value - TargetBF->getAddress());
|
||||
LLVM_DEBUG(dbgs() << "OK: real entry\n");
|
||||
} else {
|
||||
// Address in split fragment.
|
||||
BF.setHasSplitJumpTable(true);
|
||||
// Add invalid offset for proper identification of jump table size.
|
||||
addOffset(INVALID_OFFSET);
|
||||
LLVM_DEBUG(dbgs() << "OK: address in split fragment "
|
||||
<< TargetBF->getPrintName() << '\n');
|
||||
}
|
||||
if (TargetBF != &BF)
|
||||
BF.setHasIndirectTargetToSplitFragment(true);
|
||||
addEntryAddress(Value);
|
||||
}
|
||||
|
||||
// It's a jump table if the number of real entries is more than 1, or there's
|
||||
@@ -636,9 +625,11 @@ void BinaryContext::populateJumpTables() {
|
||||
for (auto JTI = JumpTables.begin(), JTE = JumpTables.end(); JTI != JTE;
|
||||
++JTI) {
|
||||
JumpTable *JT = JTI->second;
|
||||
BinaryFunction &BF = *JT->Parent;
|
||||
|
||||
if (!BF.isSimple())
|
||||
bool NonSimpleParent = false;
|
||||
for (BinaryFunction *BF : JT->Parents)
|
||||
NonSimpleParent |= !BF->isSimple();
|
||||
if (NonSimpleParent)
|
||||
continue;
|
||||
|
||||
uint64_t NextJTAddress = 0;
|
||||
@@ -646,25 +637,40 @@ void BinaryContext::populateJumpTables() {
|
||||
if (NextJTI != JTE)
|
||||
NextJTAddress = NextJTI->second->getAddress();
|
||||
|
||||
const bool Success = analyzeJumpTable(JT->getAddress(), JT->Type, BF,
|
||||
NextJTAddress, &JT->OffsetEntries);
|
||||
const bool Success =
|
||||
analyzeJumpTable(JT->getAddress(), JT->Type, *(JT->Parents[0]),
|
||||
NextJTAddress, &JT->EntriesAsAddress);
|
||||
if (!Success) {
|
||||
dbgs() << "failed to analyze jump table in function " << BF << '\n';
|
||||
LLVM_DEBUG(ListSeparator LS;
|
||||
dbgs() << "failed to analyze jump table in function ";
|
||||
for (BinaryFunction *Frag
|
||||
: JT->Parents) dbgs()
|
||||
<< LS << *Frag;
|
||||
dbgs() << '\n';);
|
||||
JT->print(dbgs());
|
||||
if (NextJTI != JTE) {
|
||||
dbgs() << "next jump table at 0x"
|
||||
<< Twine::utohexstr(NextJTI->second->getAddress())
|
||||
<< " belongs to function " << *NextJTI->second->Parent << '\n';
|
||||
LLVM_DEBUG(ListSeparator LS;
|
||||
dbgs() << "next jump table at 0x"
|
||||
<< Twine::utohexstr(NextJTI->second->getAddress())
|
||||
<< " belongs to function ";
|
||||
for (BinaryFunction *Frag
|
||||
: NextJTI->second->Parents) dbgs()
|
||||
<< LS << *Frag;
|
||||
dbgs() << "\n";);
|
||||
NextJTI->second->print(dbgs());
|
||||
}
|
||||
llvm_unreachable("jump table heuristic failure");
|
||||
}
|
||||
|
||||
for (uint64_t EntryOffset : JT->OffsetEntries) {
|
||||
if (EntryOffset == BF.getSize())
|
||||
BF.IgnoredBranches.emplace_back(EntryOffset, BF.getSize());
|
||||
else
|
||||
BF.registerReferencedOffset(EntryOffset);
|
||||
for (BinaryFunction *Frag : JT->Parents) {
|
||||
for (uint64_t EntryAddress : JT->EntriesAsAddress)
|
||||
// if target is builtin_unreachable
|
||||
if (EntryAddress == Frag->getAddress() + Frag->getSize()) {
|
||||
Frag->IgnoredBranches.emplace_back(EntryAddress - Frag->getAddress(),
|
||||
Frag->getSize());
|
||||
} else if (EntryAddress >= Frag->getAddress() &&
|
||||
EntryAddress < Frag->getAddress() + Frag->getSize()) {
|
||||
Frag->registerReferencedOffset(EntryAddress - Frag->getAddress());
|
||||
}
|
||||
}
|
||||
|
||||
// In strict mode, erase PC-relative relocation record. Later we check that
|
||||
@@ -678,8 +684,9 @@ void BinaryContext::populateJumpTables() {
|
||||
}
|
||||
|
||||
// Mark to skip the function and all its fragments.
|
||||
if (BF.hasSplitJumpTable())
|
||||
FragmentsToSkip.push_back(&BF);
|
||||
for (BinaryFunction *Frag : JT->Parents)
|
||||
if (Frag->hasIndirectTargetToSplitFragment())
|
||||
addFragmentsToSkip(Frag);
|
||||
}
|
||||
|
||||
if (opts::StrictMode && DataPCRelocations.size()) {
|
||||
@@ -695,27 +702,25 @@ void BinaryContext::populateJumpTables() {
|
||||
}
|
||||
|
||||
void BinaryContext::skipMarkedFragments() {
|
||||
// Unique functions in the vector.
|
||||
std::unordered_set<BinaryFunction *> UniqueFunctions(FragmentsToSkip.begin(),
|
||||
FragmentsToSkip.end());
|
||||
// Copy the functions back to FragmentsToSkip.
|
||||
FragmentsToSkip.assign(UniqueFunctions.begin(), UniqueFunctions.end());
|
||||
std::vector<BinaryFunction *> FragmentQueue;
|
||||
// Copy the functions to FragmentQueue.
|
||||
FragmentQueue.assign(FragmentsToSkip.begin(), FragmentsToSkip.end());
|
||||
auto addToWorklist = [&](BinaryFunction *Function) -> void {
|
||||
if (UniqueFunctions.count(Function))
|
||||
if (FragmentsToSkip.count(Function))
|
||||
return;
|
||||
FragmentsToSkip.push_back(Function);
|
||||
UniqueFunctions.insert(Function);
|
||||
FragmentQueue.push_back(Function);
|
||||
addFragmentsToSkip(Function);
|
||||
};
|
||||
// Functions containing split jump tables need to be skipped with all
|
||||
// fragments (transitively).
|
||||
for (size_t I = 0; I != FragmentsToSkip.size(); I++) {
|
||||
BinaryFunction *BF = FragmentsToSkip[I];
|
||||
assert(UniqueFunctions.count(BF) &&
|
||||
for (size_t I = 0; I != FragmentQueue.size(); I++) {
|
||||
BinaryFunction *BF = FragmentQueue[I];
|
||||
assert(FragmentsToSkip.count(BF) &&
|
||||
"internal error in traversing function fragments");
|
||||
if (opts::Verbosity >= 1)
|
||||
errs() << "BOLT-WARNING: Ignoring " << BF->getPrintName() << '\n';
|
||||
BF->setSimple(false);
|
||||
BF->setHasSplitJumpTable(true);
|
||||
BF->setHasIndirectTargetToSplitFragment(true);
|
||||
|
||||
llvm::for_each(BF->Fragments, addToWorklist);
|
||||
llvm::for_each(BF->ParentFragments, addToWorklist);
|
||||
@@ -724,7 +729,6 @@ void BinaryContext::skipMarkedFragments() {
|
||||
errs() << "BOLT-WARNING: skipped " << FragmentsToSkip.size() << " function"
|
||||
<< (FragmentsToSkip.size() == 1 ? "" : "s")
|
||||
<< " due to cold fragments\n";
|
||||
FragmentsToSkip.clear();
|
||||
}
|
||||
|
||||
MCSymbol *BinaryContext::getOrCreateGlobalSymbol(uint64_t Address, Twine Prefix,
|
||||
@@ -766,27 +770,37 @@ BinaryContext::getOrCreateJumpTable(BinaryFunction &Function, uint64_t Address,
|
||||
return (Fragment->isFragment() && Fragment->isParentFragment(Parent));
|
||||
};
|
||||
|
||||
// Two fragments of same function access same jump table
|
||||
if (JumpTable *JT = getJumpTableContainingAddress(Address)) {
|
||||
assert(JT->Type == Type && "jump table types have to match");
|
||||
bool HasMultipleParents = isFragmentOf(JT->Parent, &Function) ||
|
||||
isFragmentOf(&Function, JT->Parent);
|
||||
assert((JT->Parent == &Function || HasMultipleParents) &&
|
||||
"cannot re-use jump table of a different function");
|
||||
assert(Address == JT->getAddress() && "unexpected non-empty jump table");
|
||||
|
||||
// Flush OffsetEntries with INVALID_OFFSET if multiple parents
|
||||
// Duplicate the entry for the parent function for easy access
|
||||
if (HasMultipleParents) {
|
||||
// Prevent associating a jump table to a specific fragment twice.
|
||||
// This simple check arises from the assumption: no more than 2 fragments.
|
||||
if (JT->Parents.size() == 1 && JT->Parents[0] != &Function) {
|
||||
bool SameFunction = isFragmentOf(JT->Parents[0], &Function) ||
|
||||
isFragmentOf(&Function, JT->Parents[0]);
|
||||
assert(SameFunction &&
|
||||
"cannot re-use jump table of a different function");
|
||||
// Duplicate the entry for the parent function for easy access
|
||||
JT->Parents.push_back(&Function);
|
||||
if (opts::Verbosity > 2) {
|
||||
outs() << "BOLT-WARNING: Multiple fragments access same jump table: "
|
||||
<< JT->Parent->getPrintName() << "; " << Function.getPrintName()
|
||||
<< "\n";
|
||||
outs() << "BOLT-INFO: Multiple fragments access same jump table: "
|
||||
<< JT->Parents[0]->getPrintName() << "; "
|
||||
<< Function.getPrintName() << "\n";
|
||||
JT->print(outs());
|
||||
}
|
||||
constexpr uint64_t INVALID_OFFSET = std::numeric_limits<uint64_t>::max();
|
||||
for (unsigned I = 0; I < JT->OffsetEntries.size(); ++I)
|
||||
JT->OffsetEntries[I] = INVALID_OFFSET;
|
||||
Function.JumpTables.emplace(Address, JT);
|
||||
JT->Parents[0]->setHasIndirectTargetToSplitFragment(true);
|
||||
JT->Parents[1]->setHasIndirectTargetToSplitFragment(true);
|
||||
}
|
||||
|
||||
bool IsJumpTableParent = false;
|
||||
for (BinaryFunction *Frag : JT->Parents)
|
||||
if (Frag == &Function)
|
||||
IsJumpTableParent = true;
|
||||
assert(IsJumpTableParent &&
|
||||
"cannot re-use jump table of a different function");
|
||||
return JT->getFirstLabel();
|
||||
}
|
||||
|
||||
@@ -807,13 +821,15 @@ BinaryContext::getOrCreateJumpTable(BinaryFunction &Function, uint64_t Address,
|
||||
<< " in function " << Function << '\n');
|
||||
|
||||
JumpTable *JT = new JumpTable(*JTLabel, Address, EntrySize, Type,
|
||||
JumpTable::LabelMapType{{0, JTLabel}}, Function,
|
||||
JumpTable::LabelMapType{{0, JTLabel}},
|
||||
*getSectionForAddress(Address));
|
||||
JT->Parents.push_back(&Function);
|
||||
if (opts::Verbosity > 2)
|
||||
JT->print(outs());
|
||||
JumpTables.emplace(Address, JT);
|
||||
|
||||
// Duplicate the entry for the parent function for easy access.
|
||||
Function.JumpTables.emplace(Address, JT);
|
||||
|
||||
return JTLabel;
|
||||
}
|
||||
|
||||
@@ -835,8 +851,9 @@ BinaryContext::duplicateJumpTable(BinaryFunction &Function, JumpTable *JT,
|
||||
MCSymbol *NewLabel = Ctx->createNamedTempSymbol("duplicatedJT");
|
||||
JumpTable *NewJT =
|
||||
new JumpTable(*NewLabel, JT->getAddress(), JT->EntrySize, JT->Type,
|
||||
JumpTable::LabelMapType{{Offset, NewLabel}}, Function,
|
||||
JumpTable::LabelMapType{{Offset, NewLabel}},
|
||||
*getSectionForAddress(JT->getAddress()));
|
||||
NewJT->Parents = JT->Parents;
|
||||
NewJT->Entries = JT->Entries;
|
||||
NewJT->Counts = JT->Counts;
|
||||
uint64_t JumpTableID = ++DuplicatedJumpTables;
|
||||
|
||||
@@ -1642,11 +1642,35 @@ void BinaryFunction::postProcessJumpTables() {
|
||||
<< *this << '\n';
|
||||
}
|
||||
if (JT.Entries.empty()) {
|
||||
for (unsigned I = 0; I < JT.OffsetEntries.size(); ++I) {
|
||||
MCSymbol *Label =
|
||||
getOrCreateLocalLabel(getAddress() + JT.OffsetEntries[I],
|
||||
/*CreatePastEnd*/ true);
|
||||
JT.Entries.push_back(Label);
|
||||
bool HasOneParent = (JT.Parents.size() == 1);
|
||||
for (unsigned I = 0; I < JT.EntriesAsAddress.size(); ++I) {
|
||||
uint64_t EntryAddress = JT.EntriesAsAddress[I];
|
||||
// builtin_unreachable does not belong to any function
|
||||
// Need to handle separately
|
||||
bool IsBuiltIn = false;
|
||||
for (BinaryFunction *Parent : JT.Parents) {
|
||||
if (EntryAddress == Parent->getAddress() + Parent->getSize()) {
|
||||
IsBuiltIn = true;
|
||||
// Specify second parameter as true to accept builtin_unreachable
|
||||
MCSymbol *Label = getOrCreateLocalLabel(EntryAddress, true);
|
||||
JT.Entries.push_back(Label);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (IsBuiltIn)
|
||||
continue;
|
||||
// Create local label for targets cannot be reached by other fragments
|
||||
// Otherwise, secondary entry point to target function
|
||||
BinaryFunction *TargetBF =
|
||||
BC.getBinaryFunctionContainingAddress(EntryAddress);
|
||||
if (TargetBF->getAddress() != EntryAddress) {
|
||||
MCSymbol *Label =
|
||||
(HasOneParent && TargetBF == this)
|
||||
? getOrCreateLocalLabel(JT.EntriesAsAddress[I], true)
|
||||
: TargetBF->addEntryPointAtOffset(EntryAddress -
|
||||
TargetBF->getAddress());
|
||||
JT.Entries.push_back(Label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1672,7 +1696,8 @@ void BinaryFunction::postProcessJumpTables() {
|
||||
|
||||
uint64_t EntryOffset = JTAddress - JT->getAddress();
|
||||
while (EntryOffset < JT->getSize()) {
|
||||
uint64_t TargetOffset = JT->OffsetEntries[EntryOffset / JT->EntrySize];
|
||||
uint64_t EntryAddress = JT->EntriesAsAddress[EntryOffset / JT->EntrySize];
|
||||
uint64_t TargetOffset = EntryAddress - getAddress();
|
||||
if (TargetOffset < getSize()) {
|
||||
TakenBranches.emplace_back(JTSiteOffset, TargetOffset);
|
||||
|
||||
|
||||
@@ -29,9 +29,9 @@ extern cl::opt<unsigned> Verbosity;
|
||||
|
||||
bolt::JumpTable::JumpTable(MCSymbol &Symbol, uint64_t Address, size_t EntrySize,
|
||||
JumpTableType Type, LabelMapType &&Labels,
|
||||
BinaryFunction &BF, BinarySection &Section)
|
||||
BinarySection &Section)
|
||||
: BinaryData(Symbol, Address, 0, EntrySize, Section), EntrySize(EntrySize),
|
||||
OutputEntrySize(EntrySize), Type(Type), Labels(Labels), Parent(&BF) {}
|
||||
OutputEntrySize(EntrySize), Type(Type), Labels(Labels) {}
|
||||
|
||||
std::pair<size_t, size_t>
|
||||
bolt::JumpTable::getEntriesForAddress(const uint64_t Addr) const {
|
||||
@@ -103,11 +103,15 @@ void bolt::JumpTable::print(raw_ostream &OS) const {
|
||||
uint64_t Offset = 0;
|
||||
if (Type == JTT_PIC)
|
||||
OS << "PIC ";
|
||||
OS << "Jump table " << getName() << " for function " << *Parent << " at 0x"
|
||||
<< Twine::utohexstr(getAddress()) << " with a total count of " << Count
|
||||
<< ":\n";
|
||||
for (const uint64_t EntryOffset : OffsetEntries)
|
||||
OS << " 0x" << Twine::utohexstr(EntryOffset) << '\n';
|
||||
ListSeparator LS;
|
||||
|
||||
OS << "Jump table " << getName() << " for function ";
|
||||
for (BinaryFunction *Frag : Parents)
|
||||
OS << LS << *Frag;
|
||||
OS << " at 0x" << Twine::utohexstr(getAddress()) << " with a total count of "
|
||||
<< Count << ":\n";
|
||||
for (const uint64_t EntryAddress : EntriesAsAddress)
|
||||
OS << " absolute offset: 0x" << Twine::utohexstr(EntryAddress) << '\n';
|
||||
for (const MCSymbol *Entry : Entries) {
|
||||
auto LI = Labels.find(Offset);
|
||||
if (Offset && LI != Labels.end()) {
|
||||
|
||||
@@ -59,11 +59,10 @@ void dumpJumpTableFdata(raw_ostream &OS, const BinaryFunction &BF,
|
||||
const MCInst &Instr, const std::string &BranchLabel) {
|
||||
StringRef FunctionName = BF.getOneName();
|
||||
const JumpTable *JT = BF.getJumpTable(Instr);
|
||||
for (const uint64_t EntryOffset : JT->OffsetEntries) {
|
||||
auto LI = JT->Labels.find(EntryOffset);
|
||||
StringRef TargetName = LI->second->getName();
|
||||
const uint64_t Mispreds = JT->Counts[EntryOffset].Mispreds;
|
||||
const uint64_t Count = JT->Counts[EntryOffset].Count;
|
||||
for (uint32_t i = 0; i < JT->Entries.size(); ++i) {
|
||||
StringRef TargetName = JT->Entries[i]->getName();
|
||||
const uint64_t Mispreds = JT->Counts[i].Mispreds;
|
||||
const uint64_t Count = JT->Counts[i].Count;
|
||||
OS << "# FDATA: 1 " << FunctionName << " #" << BranchLabel << "# "
|
||||
<< "1 " << FunctionName << " #" << TargetName << "# " << Mispreds << " "
|
||||
<< Count << '\n';
|
||||
|
||||
@@ -2894,9 +2894,7 @@ void RewriteInstance::disassembleFunctions() {
|
||||
}
|
||||
|
||||
BC->processInterproceduralReferences();
|
||||
BC->clearJumpTableOffsets();
|
||||
BC->populateJumpTables();
|
||||
BC->skipMarkedFragments();
|
||||
|
||||
for (auto &BFI : BC->getBinaryFunctions()) {
|
||||
BinaryFunction &Function = BFI.second;
|
||||
@@ -2908,6 +2906,7 @@ void RewriteInstance::disassembleFunctions() {
|
||||
Function.postProcessJumpTables();
|
||||
}
|
||||
|
||||
BC->clearJumpTableTempData();
|
||||
BC->adjustCodePadding();
|
||||
|
||||
for (auto &BFI : BC->getBinaryFunctions()) {
|
||||
@@ -2918,7 +2917,7 @@ void RewriteInstance::disassembleFunctions() {
|
||||
|
||||
if (!Function.isSimple()) {
|
||||
assert((!BC->HasRelocations || Function.getSize() == 0 ||
|
||||
Function.hasSplitJumpTable()) &&
|
||||
Function.hasIndirectTargetToSplitFragment()) &&
|
||||
"unexpected non-simple function in relocation mode");
|
||||
continue;
|
||||
}
|
||||
@@ -2974,6 +2973,11 @@ void RewriteInstance::buildFunctionsCFG() {
|
||||
}
|
||||
|
||||
void RewriteInstance::postProcessFunctions() {
|
||||
// We mark fragments as non-simple here, not during disassembly,
|
||||
// So we can build their CFGs.
|
||||
BC->skipMarkedFragments();
|
||||
BC->clearFragmentsToSkip();
|
||||
|
||||
BC->TotalScore = 0;
|
||||
BC->SumExecutionCount = 0;
|
||||
for (auto &BFI : BC->getBinaryFunctions()) {
|
||||
|
||||
34
bolt/test/X86/jump-table-move.s
Normal file
34
bolt/test/X86/jump-table-move.s
Normal file
@@ -0,0 +1,34 @@
|
||||
# REQUIRES: system-linux
|
||||
|
||||
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
|
||||
# RUN: %clang %cflags -no-pie %t.o -o %t.exe -Wl,-q
|
||||
# RUN: llvm-bolt %t.exe -o %t.out --lite=0 -v=1 --jump-tables=move 2>&1 | FileCheck %s
|
||||
|
||||
# CHECK-NOT: unclaimed PC-relative relocations left in data
|
||||
# CHECK: BOLT-INFO: marking main.cold.1 as a fragment of main
|
||||
# CHECK: BOLT-WARNING: Ignoring main.cold.1
|
||||
# CHECK: BOLT-WARNING: Ignoring main
|
||||
|
||||
.text
|
||||
.globl main
|
||||
.type main, %function
|
||||
.p2align 2
|
||||
main:
|
||||
cmpl $0x67, %edi
|
||||
jne main.cold.1
|
||||
LBB0:
|
||||
retq
|
||||
.size main, .-main
|
||||
|
||||
.globl main.cold.1
|
||||
.type main.cold.1, %function
|
||||
.p2align 2
|
||||
main.cold.1:
|
||||
jmpq *JUMP_TABLE(,%rcx,8)
|
||||
.size main.cold.1, .-main.cold.1
|
||||
|
||||
.rodata
|
||||
.globl JUMP_TABLE
|
||||
JUMP_TABLE:
|
||||
.quad LBB0
|
||||
.quad LBB0
|
||||
@@ -8,9 +8,10 @@
|
||||
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
|
||||
# RUN: llvm-strip --strip-unneeded %t.o
|
||||
# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q
|
||||
# RUN: llvm-bolt -v=3 %t.exe -o %t.out 2>&1 | FileCheck %s
|
||||
# RUN: llvm-bolt -print-cfg -v=3 %t.exe -o %t.out 2>&1 | FileCheck %s
|
||||
|
||||
# CHECK: BOLT-WARNING: Multiple fragments access same jump table: main; main.cold.1
|
||||
# CHECK: BOLT-INFO: Multiple fragments access same jump table: main; main.cold.1
|
||||
# CHECK: PIC Jump table JUMP_TABLE1 for function main, main.cold.1 at {{.*}} with a total count of 0:
|
||||
|
||||
.text
|
||||
.globl main
|
||||
|
||||
Reference in New Issue
Block a user