Skip to content

Commit 59721f2

Browse files
authored
[MIPS] Optimize sortRelocs for o32
The o32 ABI specifies: > Each relocation type of R_MIPS_HI16 must have an associated R_MIPS_LO16 entry immediately following it in the list of relocations. [...] the addend AHL is computed as (AHI << 16) + (short)ALO In practice, the high-part and low-part relocations may not be adjacent in assembly files, requiring the assembler to reorder relocations. http://reviews.llvm.org/D19718 performed the reordering, but did not optimize for the common case where a %lo immediately follows its matching %hi. The quadratic time complexity could make sections with many relocations very slow to process. This patch implements the fast path, simplifies the code, and makes the behavior more similar to GNU assembler (for the .rel.mips_hilo_8b test). We also remove `OriginalSymbol`, removing overhead for other targets. Fix #104562 Pull Request: #104723
1 parent 84aa02d commit 59721f2

File tree

4 files changed

+49
-96
lines changed

4 files changed

+49
-96
lines changed

llvm/include/llvm/MC/MCELFObjectWriter.h

+3-5
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,14 @@ struct ELFRelocationEntry {
3737
const MCSymbolELF *Symbol; // The symbol to relocate with.
3838
unsigned Type; // The type of the relocation.
3939
uint64_t Addend; // The addend to use.
40-
const MCSymbolELF *OriginalSymbol; // The original value of Symbol if we changed it.
4140

4241
ELFRelocationEntry(uint64_t Offset, const MCSymbolELF *Symbol, unsigned Type,
43-
uint64_t Addend, const MCSymbolELF *OriginalSymbol)
44-
: Offset(Offset), Symbol(Symbol), Type(Type), Addend(Addend),
45-
OriginalSymbol(OriginalSymbol) {}
42+
uint64_t Addend)
43+
: Offset(Offset), Symbol(Symbol), Type(Type), Addend(Addend) {}
4644

4745
void print(raw_ostream &Out) const {
4846
Out << "Off=" << Offset << ", Sym=" << Symbol << ", Type=" << Type
49-
<< ", Addend=" << Addend << ", OriginalSymbol=" << OriginalSymbol;
47+
<< ", Addend=" << Addend;
5048
}
5149

5250
LLVM_DUMP_METHOD void dump() const { print(errs()); }

llvm/lib/MC/ELFObjectWriter.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -1453,7 +1453,7 @@ void ELFObjectWriter::recordRelocation(MCAssembler &Asm,
14531453
SecA ? cast<MCSymbolELF>(SecA->getBeginSymbol()) : nullptr;
14541454
if (SectionSymbol)
14551455
SectionSymbol->setUsedInReloc();
1456-
ELFRelocationEntry Rec(FixupOffset, SectionSymbol, Type, Addend, SymA);
1456+
ELFRelocationEntry Rec(FixupOffset, SectionSymbol, Type, Addend);
14571457
Relocations[&FixupSection].push_back(Rec);
14581458
return;
14591459
}
@@ -1468,7 +1468,7 @@ void ELFObjectWriter::recordRelocation(MCAssembler &Asm,
14681468
else
14691469
RenamedSymA->setUsedInReloc();
14701470
}
1471-
ELFRelocationEntry Rec(FixupOffset, RenamedSymA, Type, Addend, SymA);
1471+
ELFRelocationEntry Rec(FixupOffset, RenamedSymA, Type, Addend);
14721472
Relocations[&FixupSection].push_back(Rec);
14731473
}
14741474

llvm/lib/Target/Mips/MCTargetDesc/MipsELFObjectWriter.cpp

+41-86
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,6 @@ struct MipsRelocationEntry {
4040
bool Matched = false; ///< Is this relocation part of a match.
4141

4242
MipsRelocationEntry(const ELFRelocationEntry &R) : R(R) {}
43-
44-
void print(raw_ostream &Out) const {
45-
R.print(Out);
46-
Out << ", Matched=" << Matched;
47-
}
4843
};
4944

5045
class MipsELFObjectWriter : public MCELFObjectTargetWriter {
@@ -134,8 +129,7 @@ static unsigned getMatchingLoType(const ELFRelocationEntry &Reloc) {
134129
if (Type == ELF::R_MIPS16_HI16)
135130
return ELF::R_MIPS16_LO16;
136131

137-
if (Reloc.OriginalSymbol &&
138-
Reloc.OriginalSymbol->getBinding() != ELF::STB_LOCAL)
132+
if (Reloc.Symbol && Reloc.Symbol->getBinding() != ELF::STB_LOCAL)
139133
return ELF::R_MIPS_NONE;
140134

141135
if (Type == ELF::R_MIPS_GOT16)
@@ -148,43 +142,11 @@ static unsigned getMatchingLoType(const ELFRelocationEntry &Reloc) {
148142
return ELF::R_MIPS_NONE;
149143
}
150144

151-
/// Determine whether a relocation (X) matches the one given in R.
152-
///
153-
/// A relocation matches if:
154-
/// - It's type matches that of a corresponding low part. This is provided in
155-
/// MatchingType for efficiency.
156-
/// - It's based on the same symbol.
157-
/// - It's offset of greater or equal to that of the one given in R.
158-
/// It should be noted that this rule assumes the programmer does not use
159-
/// offsets that exceed the alignment of the symbol. The carry-bit will be
160-
/// incorrect if this is not true.
161-
///
162-
/// A matching relocation is unbeatable if:
163-
/// - It is not already involved in a match.
164-
/// - It's offset is exactly that of the one given in R.
165-
static FindBestPredicateResult isMatchingReloc(const MipsRelocationEntry &X,
166-
const ELFRelocationEntry &R,
167-
unsigned MatchingType) {
168-
if (X.R.Type == MatchingType && X.R.OriginalSymbol == R.OriginalSymbol) {
169-
if (!X.Matched && X.R.Addend == R.Addend)
170-
return FindBest_PerfectMatch;
171-
else if (X.R.Addend >= R.Addend)
172-
return FindBest_Match;
173-
}
174-
return FindBest_NoMatch;
175-
}
176-
177-
/// Determine whether Candidate or PreviousBest is the better match.
178-
/// The return value is true if Candidate is the better match.
179-
///
180-
/// A matching relocation is a better match if:
181-
/// - It has a smaller addend.
182-
/// - It is not already involved in a match.
183-
static bool compareMatchingRelocs(const MipsRelocationEntry &Candidate,
184-
const MipsRelocationEntry &PreviousBest) {
185-
if (Candidate.R.Addend != PreviousBest.R.Addend)
186-
return Candidate.R.Addend < PreviousBest.R.Addend;
187-
return PreviousBest.Matched && !Candidate.Matched;
145+
// Determine whether a relocation X is a low-part and matches the high-part R
146+
// perfectly by symbol and addend.
147+
static bool isMatchingReloc(unsigned MatchingType, const ELFRelocationEntry &R,
148+
const ELFRelocationEntry &X) {
149+
return X.Type == MatchingType && X.Symbol == R.Symbol && X.Addend == R.Addend;
188150
}
189151

190152
MipsELFObjectWriter::MipsELFObjectWriter(uint8_t OSABI,
@@ -413,58 +375,51 @@ void MipsELFObjectWriter::sortRelocs(const MCAssembler &Asm,
413375
if (hasRelocationAddend())
414376
return;
415377

416-
if (Relocs.size() < 2)
417-
return;
418-
419378
// Sort relocations by the address they are applied to.
420379
llvm::sort(Relocs,
421380
[](const ELFRelocationEntry &A, const ELFRelocationEntry &B) {
422381
return A.Offset < B.Offset;
423382
});
424383

384+
// Place relocations in a list for reorder convenience. Hi16 contains the
385+
// iterators of high-part relocations.
425386
std::list<MipsRelocationEntry> Sorted;
426-
std::list<ELFRelocationEntry> Remainder;
427-
428-
// Separate the movable relocations (AHL relocations using the high bits) from
429-
// the immobile relocations (everything else). This does not preserve high/low
430-
// matches that already existed in the input.
431-
copy_if_else(Relocs.begin(), Relocs.end(), std::back_inserter(Remainder),
432-
std::back_inserter(Sorted), [](const ELFRelocationEntry &Reloc) {
433-
return getMatchingLoType(Reloc) != ELF::R_MIPS_NONE;
434-
});
387+
SmallVector<std::list<MipsRelocationEntry>::iterator, 0> Hi16;
388+
for (auto &R : Relocs) {
389+
Sorted.push_back(R);
390+
if (getMatchingLoType(R) != ELF::R_MIPS_NONE)
391+
Hi16.push_back(std::prev(Sorted.end()));
392+
}
435393

436-
for (auto &R : Remainder) {
394+
for (auto I : Hi16) {
395+
auto &R = I->R;
437396
unsigned MatchingType = getMatchingLoType(R);
438-
assert(MatchingType != ELF::R_MIPS_NONE &&
439-
"Wrong list for reloc that doesn't need a match");
440-
441-
// Find the best matching relocation for the current high part.
442-
// See isMatchingReloc for a description of a matching relocation and
443-
// compareMatchingRelocs for a description of what 'best' means.
444-
auto InsertionPoint =
445-
find_best(Sorted.begin(), Sorted.end(),
446-
[&R, &MatchingType](const MipsRelocationEntry &X) {
447-
return isMatchingReloc(X, R, MatchingType);
448-
},
449-
compareMatchingRelocs);
450-
451-
// If we matched then insert the high part in front of the match and mark
452-
// both relocations as being involved in a match. We only mark the high
453-
// part for cosmetic reasons in the debug output.
397+
// If the next relocation is a perfect match, continue;
398+
if (std::next(I) != Sorted.end() &&
399+
isMatchingReloc(MatchingType, R, std::next(I)->R))
400+
continue;
401+
// Otherwise, find the best matching low-part relocation with the following
402+
// criteria. It must have the same symbol and its addend is no lower than
403+
// that of the current high-part.
454404
//
455-
// If we failed to find a match then the high part is orphaned. This is not
456-
// permitted since the relocation cannot be evaluated without knowing the
457-
// carry-in. We can sometimes handle this using a matching low part that is
458-
// already used in a match but we already cover that case in
459-
// isMatchingReloc and compareMatchingRelocs. For the remaining cases we
460-
// should insert the high part at the end of the list. This will cause the
461-
// linker to fail but the alternative is to cause the linker to bind the
462-
// high part to a semi-matching low part and silently calculate the wrong
463-
// value. Unfortunately we have no means to warn the user that we did this
464-
// so leave it up to the linker to complain about it.
465-
if (InsertionPoint != Sorted.end())
466-
InsertionPoint->Matched = true;
467-
Sorted.insert(InsertionPoint, R)->Matched = true;
405+
// (1) %lo with a smaller offset is preferred.
406+
// (2) %lo with the same offset that is unmatched is preferred.
407+
// (3) later %lo is preferred.
408+
auto Best = Sorted.end();
409+
for (auto J = Sorted.begin(); J != Sorted.end(); ++J) {
410+
auto &R1 = J->R;
411+
if (R1.Type == MatchingType && R.Symbol == R1.Symbol &&
412+
R.Addend <= R1.Addend &&
413+
(Best == Sorted.end() || R1.Addend < Best->R.Addend ||
414+
(!Best->Matched && R1.Addend == Best->R.Addend)))
415+
Best = J;
416+
}
417+
if (Best != Sorted.end() && R.Addend == Best->R.Addend)
418+
Best->Matched = true;
419+
420+
// Move the high-part before the low-part, or if not found, the end of the
421+
// list. The unmatched high-part will lead to a linker warning/error.
422+
Sorted.splice(Best, Sorted, I);
468423
}
469424

470425
assert(Relocs.size() == Sorted.size() && "Some relocs were not consumed");

llvm/test/MC/Mips/sort-relocation-table.s

+3-3
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,8 @@
150150
lui $2, %hi(sym1)
151151

152152
# CHECK-LABEL: Section ({{[0-9]+}}) .rel.mips_hilo_8b {
153-
# CHECK-NEXT: 0x8 R_MIPS_HI16 sym1
154153
# CHECK-NEXT: 0x0 R_MIPS_LO16 sym1
154+
# CHECK-NEXT: 0x8 R_MIPS_HI16 sym1
155155
# CHECK-NEXT: 0x4 R_MIPS_LO16 sym1
156156
# CHECK-NEXT: }
157157

@@ -331,8 +331,8 @@
331331
lui $2, %got(local1)
332332

333333
# CHECK-LABEL: Section ({{[0-9]+}}) .rel.mips_gotlo_8b {
334-
# CHECK-NEXT: 0x8 R_MIPS_GOT16 .text
335334
# CHECK-NEXT: 0x0 R_MIPS_LO16 .text
335+
# CHECK-NEXT: 0x8 R_MIPS_GOT16 .text
336336
# CHECK-NEXT: 0x4 R_MIPS_LO16 .text
337337
# CHECK-NEXT: }
338338

@@ -372,9 +372,9 @@
372372
# CHECK-LABEL: Section ({{[0-9]+}}) .rel.mips_gotlo_10 {
373373
# CHECK-NEXT: 0x0 R_MIPS_GOT16 .text
374374
# CHECK-NEXT: 0x4 R_MIPS_LO16 .text
375+
# CHECK-NEXT: 0x8 R_MIPS_GOT16 .text
375376
# CHECK-NEXT: 0xC R_MIPS_GOT16 .text
376377
# CHECK-NEXT: 0x10 R_MIPS_LO16 .text
377-
# CHECK-NEXT: 0x8 R_MIPS_GOT16 .text
378378
# CHECK-NEXT: }
379379

380380
# Finally, do test 2 for R_MIPS_GOT16 on external symbols to prove they are

0 commit comments

Comments
 (0)