Skip to content

Commit 8244ff6

Browse files
authored
[BOLT] Fix incorrect basic block output addresses (#70000)
Some optimization passes may duplicate basic blocks and assign the same input offset to a number of different blocks in a function. This is done e.g. to correctly map debugging ranges for duplicated code. However, duplicate input offsets present a problem when we use AddressMap to generate new addresses for basic blocks. The output address is calculated based on the input offset and will be the same for blocks with identical offsets. The result is potentially incorrect debug info and BAT records. To address the issue, we have to eliminate the dependency on input offsets while generating output addresses for a basic block. Each block has a unique label, hence we extend AddressMap to include address lookup based on MCSymbol and use the new functionality to update block addresses.
1 parent 888f070 commit 8244ff6

File tree

5 files changed

+120
-46
lines changed

5 files changed

+120
-46
lines changed

bolt/include/bolt/Core/AddressMap.h

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,16 @@
66
//
77
//===----------------------------------------------------------------------===//
88
//
9-
// Helper class to create a mapping from input to output addresses needed for
10-
// updating debugging symbols and BAT. We emit an MCSection containing
11-
// <Input address, Output MCSymbol> pairs to the object file and JITLink will
12-
// transform this in <Input address, Output address> pairs. The linker output
13-
// can then be parsed and used to establish the mapping.
9+
// This file contains the declaration of the AddressMap class used for looking
10+
// up addresses in the output object.
1411
//
1512
//===----------------------------------------------------------------------===//
16-
//
13+
1714
#ifndef BOLT_CORE_ADDRESS_MAP_H
1815
#define BOLT_CORE_ADDRESS_MAP_H
1916

2017
#include "llvm/ADT/StringRef.h"
18+
#include "llvm/MC/MCSymbol.h"
2119

2220
#include <optional>
2321
#include <unordered_map>
@@ -30,26 +28,48 @@ namespace bolt {
3028

3129
class BinaryContext;
3230

31+
/// Helper class to create a mapping from input entities to output addresses
32+
/// needed for updating debugging symbols and BAT. We emit a section containing
33+
/// <Input entity, Output MCSymbol> pairs to the object file and JITLink will
34+
/// transform this in <Input entity, Output address> pairs. The linker output
35+
/// can then be parsed and used to establish the mapping.
36+
///
37+
/// The entities that can be mapped to output address are input addresses and
38+
/// labels (MCSymbol). Input addresses support one-to-many mapping.
3339
class AddressMap {
34-
using MapTy = std::unordered_multimap<uint64_t, uint64_t>;
35-
MapTy Map;
40+
static const char *const AddressSectionName;
41+
static const char *const LabelSectionName;
3642

37-
public:
38-
static const char *const SectionName;
43+
/// Map multiple <input address> to <output address>.
44+
using Addr2AddrMapTy = std::unordered_multimap<uint64_t, uint64_t>;
45+
Addr2AddrMapTy Address2AddressMap;
3946

47+
/// Map MCSymbol to its output address. Normally used for temp symbols that
48+
/// are not updated by the linker.
49+
using Label2AddrMapTy = DenseMap<const MCSymbol *, uint64_t>;
50+
Label2AddrMapTy Label2AddrMap;
51+
52+
public:
4053
static void emit(MCStreamer &Streamer, BinaryContext &BC);
41-
static AddressMap parse(StringRef Buffer, const BinaryContext &BC);
54+
static std::optional<AddressMap> parse(BinaryContext &BC);
4255

4356
std::optional<uint64_t> lookup(uint64_t InputAddress) const {
44-
auto It = Map.find(InputAddress);
45-
if (It != Map.end())
57+
auto It = Address2AddressMap.find(InputAddress);
58+
if (It != Address2AddressMap.end())
59+
return It->second;
60+
return std::nullopt;
61+
}
62+
63+
std::optional<uint64_t> lookup(const MCSymbol *Symbol) const {
64+
auto It = Label2AddrMap.find(Symbol);
65+
if (It != Label2AddrMap.end())
4666
return It->second;
4767
return std::nullopt;
4868
}
4969

50-
std::pair<MapTy::const_iterator, MapTy::const_iterator>
70+
std::pair<Addr2AddrMapTy::const_iterator, Addr2AddrMapTy::const_iterator>
5171
lookupAll(uint64_t InputAddress) const {
52-
return Map.equal_range(InputAddress);
72+
return Address2AddressMap.equal_range(InputAddress);
5373
}
5474
};
5575

bolt/lib/Core/AddressMap.cpp

Lines changed: 74 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,44 @@
1+
//===- bolt/Core/AddressMap.cpp - Input-output Address Map ----------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
19
#include "bolt/Core/AddressMap.h"
210
#include "bolt/Core/BinaryContext.h"
311
#include "bolt/Core/BinaryFunction.h"
12+
#include "bolt/Core/BinarySection.h"
413
#include "llvm/MC/MCStreamer.h"
514
#include "llvm/Support/DataExtractor.h"
615

716
namespace llvm {
817
namespace bolt {
918

10-
const char *const AddressMap::SectionName = ".bolt.address_map";
19+
const char *const AddressMap::AddressSectionName = ".bolt.addr2addr_map";
20+
const char *const AddressMap::LabelSectionName = ".bolt.label2addr_map";
1121

12-
static void emitLabel(MCStreamer &Streamer, uint64_t InputAddress,
13-
const MCSymbol *OutputLabel) {
22+
static void emitAddress(MCStreamer &Streamer, uint64_t InputAddress,
23+
const MCSymbol *OutputLabel) {
1424
Streamer.emitIntValue(InputAddress, 8);
1525
Streamer.emitSymbolValue(OutputLabel, 8);
1626
}
1727

28+
static void emitLabel(MCStreamer &Streamer, const MCSymbol *OutputLabel) {
29+
Streamer.emitIntValue(reinterpret_cast<uint64_t>(OutputLabel), 8);
30+
Streamer.emitSymbolValue(OutputLabel, 8);
31+
}
32+
1833
void AddressMap::emit(MCStreamer &Streamer, BinaryContext &BC) {
19-
Streamer.switchSection(BC.getDataSection(SectionName));
34+
// Mark map sections as link-only to avoid allocation in the output file.
35+
const unsigned Flags = BinarySection::getFlags(/*IsReadOnly*/ true,
36+
/*IsText*/ false,
37+
/*IsAllocatable*/ true);
38+
BC.registerOrUpdateSection(AddressSectionName, ELF::SHT_PROGBITS, Flags)
39+
.setLinkOnly();
40+
BC.registerOrUpdateSection(LabelSectionName, ELF::SHT_PROGBITS, Flags)
41+
.setLinkOnly();
2042

2143
for (const auto &[BFAddress, BF] : BC.getBinaryFunctions()) {
2244
if (!BF.requiresAddressMap())
@@ -26,37 +48,69 @@ void AddressMap::emit(MCStreamer &Streamer, BinaryContext &BC) {
2648
if (!BB.getLabel()->isDefined())
2749
continue;
2850

29-
emitLabel(Streamer, BFAddress + BB.getInputAddressRange().first,
30-
BB.getLabel());
51+
Streamer.switchSection(BC.getDataSection(LabelSectionName));
52+
emitLabel(Streamer, BB.getLabel());
3153

3254
if (!BB.hasLocSyms())
3355
continue;
3456

57+
Streamer.switchSection(BC.getDataSection(AddressSectionName));
3558
for (auto [Offset, Symbol] : BB.getLocSyms())
36-
emitLabel(Streamer, BFAddress + Offset, Symbol);
59+
emitAddress(Streamer, BFAddress + Offset, Symbol);
3760
}
3861
}
3962
}
4063

41-
AddressMap AddressMap::parse(StringRef Buffer, const BinaryContext &BC) {
42-
const auto EntrySize = 2 * BC.AsmInfo->getCodePointerSize();
43-
assert(Buffer.size() % EntrySize == 0 && "Unexpected address map size");
64+
std::optional<AddressMap> AddressMap::parse(BinaryContext &BC) {
65+
auto AddressMapSection = BC.getUniqueSectionByName(AddressSectionName);
66+
auto LabelMapSection = BC.getUniqueSectionByName(LabelSectionName);
4467

45-
DataExtractor DE(Buffer, BC.AsmInfo->isLittleEndian(),
46-
BC.AsmInfo->getCodePointerSize());
47-
DataExtractor::Cursor Cursor(0);
68+
if (!AddressMapSection && !LabelMapSection)
69+
return std::nullopt;
4870

4971
AddressMap Parsed;
50-
Parsed.Map.reserve(Buffer.size() / EntrySize);
5172

52-
while (Cursor && !DE.eof(Cursor)) {
53-
const auto Input = DE.getAddress(Cursor);
54-
const auto Output = DE.getAddress(Cursor);
55-
if (!Parsed.Map.count(Input))
56-
Parsed.Map.insert({Input, Output});
73+
const size_t EntrySize = 2 * BC.AsmInfo->getCodePointerSize();
74+
auto parseSection =
75+
[&](BinarySection &Section,
76+
function_ref<void(uint64_t, uint64_t)> InsertCallback) {
77+
StringRef Buffer = Section.getOutputContents();
78+
assert(Buffer.size() % EntrySize == 0 && "Unexpected address map size");
79+
80+
DataExtractor DE(Buffer, BC.AsmInfo->isLittleEndian(),
81+
BC.AsmInfo->getCodePointerSize());
82+
DataExtractor::Cursor Cursor(0);
83+
84+
while (Cursor && !DE.eof(Cursor)) {
85+
const uint64_t Input = DE.getAddress(Cursor);
86+
const uint64_t Output = DE.getAddress(Cursor);
87+
InsertCallback(Input, Output);
88+
}
89+
90+
assert(Cursor && "Error reading address map section");
91+
BC.deregisterSection(Section);
92+
};
93+
94+
if (AddressMapSection) {
95+
Parsed.Address2AddressMap.reserve(AddressMapSection->getOutputSize() /
96+
EntrySize);
97+
parseSection(*AddressMapSection, [&](uint64_t Input, uint64_t Output) {
98+
if (!Parsed.Address2AddressMap.count(Input))
99+
Parsed.Address2AddressMap.insert({Input, Output});
100+
});
101+
}
102+
103+
if (LabelMapSection) {
104+
Parsed.Label2AddrMap.reserve(LabelMapSection->getOutputSize() / EntrySize);
105+
parseSection(*LabelMapSection, [&](uint64_t Input, uint64_t Output) {
106+
assert(!Parsed.Label2AddrMap.count(
107+
reinterpret_cast<const MCSymbol *>(Input)) &&
108+
"Duplicate label entry detected.");
109+
Parsed.Label2AddrMap.insert(
110+
{reinterpret_cast<const MCSymbol *>(Input), Output});
111+
});
57112
}
58113

59-
assert(Cursor && "Error reading address map section");
60114
return Parsed;
61115
}
62116

bolt/lib/Core/BinaryFunction.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4165,15 +4165,20 @@ void BinaryFunction::updateOutputValues(const BOLTLinker &Linker) {
41654165
// Injected functions likely will fail lookup, as they have no
41664166
// input range. Just assign the BB the output address of the
41674167
// function.
4168-
auto MaybeBBAddress =
4169-
BC.getIOAddressMap().lookup(BB->getInputOffset() + getAddress());
4168+
auto MaybeBBAddress = BC.getIOAddressMap().lookup(BB->getLabel());
41704169
const uint64_t BBAddress = MaybeBBAddress ? *MaybeBBAddress
41714170
: BB->isSplit() ? FF.getAddress()
41724171
: getOutputAddress();
41734172
BB->setOutputStartAddress(BBAddress);
41744173

4175-
if (PrevBB)
4174+
if (PrevBB) {
4175+
assert(PrevBB->getOutputAddressRange().first <= BBAddress &&
4176+
"Bad output address for basic block.");
4177+
assert((PrevBB->getOutputAddressRange().first != BBAddress ||
4178+
!hasInstructions() || PrevBB->empty()) &&
4179+
"Bad output address for basic block.");
41764180
PrevBB->setOutputEndAddress(BBAddress);
4181+
}
41774182
PrevBB = BB;
41784183
}
41794184

bolt/lib/Rewrite/RewriteInstance.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3196,9 +3196,6 @@ void RewriteInstance::preregisterSections() {
31963196
ROFlags);
31973197
BC->registerOrUpdateSection(getNewSecPrefix() + ".rodata.cold",
31983198
ELF::SHT_PROGBITS, ROFlags);
3199-
BC->registerOrUpdateSection(AddressMap::SectionName, ELF::SHT_PROGBITS,
3200-
ROFlags)
3201-
.setLinkOnly();
32023199
}
32033200

32043201
void RewriteInstance::emitAndLink() {
@@ -3669,11 +3666,8 @@ void RewriteInstance::mapAllocatableSections(
36693666
}
36703667

36713668
void RewriteInstance::updateOutputValues(const BOLTLinker &Linker) {
3672-
if (auto MapSection = BC->getUniqueSectionByName(AddressMap::SectionName)) {
3673-
auto Map = AddressMap::parse(MapSection->getOutputContents(), *BC);
3674-
BC->setIOAddressMap(std::move(Map));
3675-
BC->deregisterSection(*MapSection);
3676-
}
3669+
if (std::optional<AddressMap> Map = AddressMap::parse(*BC))
3670+
BC->setIOAddressMap(std::move(*Map));
36773671

36783672
for (BinaryFunction *Function : BC->getAllBinaryFunctions())
36793673
Function->updateOutputValues(Linker);

bolt/test/X86/jump-table-icp.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ RUN: (llvm-bolt %t.exe --data %t.fdata -o %t --relocs \
1212
RUN: --reorder-blocks=cache --split-functions --split-all-cold \
1313
RUN: --use-gnu-stack --dyno-stats --indirect-call-promotion=jump-tables \
1414
RUN: --print-icp -v=0 \
15+
RUN: --enable-bat --print-cache-metrics \
1516
RUN: --icp-jt-remaining-percent-threshold=10 \
1617
RUN: --icp-jt-total-percent-threshold=2 \
1718
RUN: --indirect-call-promotion-topn=1 \

0 commit comments

Comments
 (0)