Skip to content

Commit bb72424

Browse files
authored
[MC] Use StringTable to reduce dynamic relocations (#144202)
Dynamic relocations are expensive on ELF/Linux platforms because they are applied in userspace on process startup. Therefore, it is worth optimizing them to make PIE and PIC dylib builds faster. In +asserts builds (non-NDEBUG), nikic identified these schedule class name string pointers as the leading source of dynamic relocations. [1] This change uses llvm::StringTable and the StringToOffsetTable TableGen helper to turn the string pointers into 32-bit offsets into a separate character array. The number of dynamic relocations is reduced by ~60%: ❯ llvm-readelf --dyn-relocations lib/libLLVM.so | wc -l 381376 # before 155156 # after The test suite time is modestly affected, but I'm running on a shared noisy workstation VM with a ton of cores: https://gist.github.com/rnk/f38882c2fe2e63d0eb58b8fffeab69de Testing Time: 100.88s # before Testing Time: 78.50s. # after Testing Time: 96.25s. # before again I haven't used any fancy hyperfine/denoising tools, but I think the result is clearly visible and we should ship it. [1] https://gist.github.com/nikic/554f0a544ca15d5219788f1030f78c5a
1 parent 8231dd7 commit bb72424

File tree

7 files changed

+52
-25
lines changed

7 files changed

+52
-25
lines changed

llvm/include/llvm/MC/MCSchedule.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#define LLVM_MC_MCSCHEDULE_H
1616

1717
#include "llvm/ADT/StringRef.h"
18+
#include "llvm/ADT/StringTable.h"
1819
#include "llvm/MC/MCInstrDesc.h"
1920
#include "llvm/Support/Compiler.h"
2021
#include "llvm/Support/ErrorHandling.h"
@@ -124,7 +125,7 @@ struct MCSchedClassDesc {
124125
static const unsigned short VariantNumMicroOps = InvalidNumMicroOps - 1;
125126

126127
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
127-
const char* Name;
128+
uint32_t NameOff;
128129
#endif
129130
uint16_t NumMicroOps : 13;
130131
uint16_t BeginGroup : 1;
@@ -324,6 +325,7 @@ struct MCSchedModel {
324325
const MCSchedClassDesc *SchedClassTable;
325326
unsigned NumProcResourceKinds;
326327
unsigned NumSchedClasses;
328+
const StringTable *SchedClassNames;
327329
// Instruction itinerary tables used by InstrItineraryData.
328330
friend class InstrItineraryData;
329331
const InstrItinerary *InstrItineraries;
@@ -368,6 +370,14 @@ struct MCSchedModel {
368370
return &SchedClassTable[SchedClassIdx];
369371
}
370372

373+
StringRef getSchedClassName(unsigned SchedClassIdx) const {
374+
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
375+
return (*SchedClassNames)[SchedClassTable[SchedClassIdx].NameOff];
376+
#else
377+
return "<unknown>";
378+
#endif
379+
}
380+
371381
/// Returns the latency value for the scheduling class.
372382
LLVM_ABI static int computeInstrLatency(const MCSubtargetInfo &STI,
373383
const MCSchedClassDesc &SCDesc);

llvm/lib/MC/MCSchedule.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const MCSchedModel MCSchedModel::Default = {DefaultIssueWidth,
3737
0,
3838
0,
3939
nullptr,
40+
nullptr,
4041
nullptr};
4142

4243
int MCSchedModel::computeInstrLatency(const MCSubtargetInfo &STI,

llvm/lib/MCA/InstrBuilder.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ static void initializeUsedResources(InstrDesc &ID,
7575
WithColor::warning()
7676
<< "Ignoring invalid write of zero cycles on processor resource "
7777
<< PR.Name << "\n";
78-
WithColor::note() << "found in scheduling class " << SCDesc.Name
78+
WithColor::note() << "found in scheduling class "
79+
<< SM.getSchedClassName(ID.SchedClassID)
7980
<< " (write index #" << I << ")\n";
8081
#endif
8182
continue;

llvm/test/TableGen/CompressWriteLatencyEntry.td

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ def Read_D : SchedRead;
3333
// CHECK-NEXT: }; // MyTargetReadAdvanceTable
3434

3535
// CHECK: static const llvm::MCSchedClassDesc SchedModel_ASchedClasses[] = {
36-
// CHECK-NEXT: {DBGFIELD("InvalidSchedClass") 8191, false, false, false, 0, 0, 0, 0, 0, 0},
37-
// CHECK-NEXT: {DBGFIELD("Inst_A") 1, false, false, false, 0, 0, 1, 1, 0, 0}, // #1
38-
// CHECK-NEXT: {DBGFIELD("Inst_B") 1, false, false, false, 0, 0, 2, 1, 0, 0}, // #2
39-
// CHECK-NEXT: {DBGFIELD("Inst_C") 1, false, false, false, 0, 0, 1, 1, 1, 1}, // #3
36+
// CHECK-NEXT: {DBGFIELD(1) 8191, false, false, false, 0, 0, 0, 0, 0, 0},
37+
// CHECK-NEXT: {DBGFIELD(/*Inst_A*/ {{[0-9]+}}) 1, false, false, false, 0, 0, 1, 1, 0, 0}, // #1
38+
// CHECK-NEXT: {DBGFIELD(/*Inst_B*/ {{[0-9]+}}) 1, false, false, false, 0, 0, 2, 1, 0, 0}, // #2
39+
// CHECK-NEXT: {DBGFIELD(/*Inst_C*/ {{[0-9]+}}) 1, false, false, false, 0, 0, 1, 1, 1, 1}, // #3
4040
// CHECK-NEXT: }; // SchedModel_ASchedClasses
4141

4242
let SchedModel = SchedModel_A in {

llvm/test/TableGen/InvalidMCSchedClassDesc.td

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
// RUN: llvm-tblgen -gen-subtarget -I %p/../../include %s 2>&1 | FileCheck %s
2-
// Check if it is valid MCSchedClassDesc if didn't have the resources.
2+
// Check if it is valid MCSchedClassDesc if didn't have the resources.
33

44
include "llvm/Target/Target.td"
55

66
def MyTarget : Target;
77

88
let OutOperandList = (outs), InOperandList = (ins) in {
9-
def Inst_A : Instruction;
10-
def Inst_B : Instruction;
9+
def Inst_A : Instruction;
10+
def Inst_B : Instruction;
1111
}
1212

1313
let CompleteModel = 0 in {
@@ -18,27 +18,27 @@ let CompleteModel = 0 in {
1818

1919
// Inst_B didn't have the resoures, and it is invalid.
2020
// CHECK: SchedModel_ASchedClasses[] = {
21-
// CHECK: {DBGFIELD("Inst_A") 1
22-
// CHECK-NEXT: {DBGFIELD("Inst_B") 8191
21+
// CHECK: {DBGFIELD(/*Inst_A*/ 19) 1
22+
// CHECK-NEXT: {DBGFIELD(/*Inst_B*/ 26) 8191
2323
let SchedModel = SchedModel_A in {
2424
def Write_A : SchedWriteRes<[]>;
2525
def : InstRW<[Write_A], (instrs Inst_A)>;
2626
}
2727

2828
// Inst_A didn't have the resoures, and it is invalid.
2929
// CHECK: SchedModel_BSchedClasses[] = {
30-
// CHECK: {DBGFIELD("Inst_A") 8191
31-
// CHECK-NEXT: {DBGFIELD("Inst_B") 1
30+
// CHECK: {DBGFIELD(/*Inst_A*/ 19) 8191
31+
// CHECK-NEXT: {DBGFIELD(/*Inst_B*/ 26) 1
3232
let SchedModel = SchedModel_B in {
33-
def Write_B: SchedWriteRes<[]>;
33+
def Write_B: SchedWriteRes<[]>;
3434
def : InstRW<[Write_B], (instrs Inst_B)>;
3535
}
3636

3737
// CHECK: SchedModel_CSchedClasses[] = {
38-
// CHECK: {DBGFIELD("Inst_A") 1
39-
// CHECK-NEXT: {DBGFIELD("Inst_B") 1
38+
// CHECK: {DBGFIELD(/*Inst_A*/ 19) 1
39+
// CHECK-NEXT: {DBGFIELD(/*Inst_B*/ 26) 1
4040
let SchedModel = SchedModel_C in {
41-
def Write_C: SchedWriteRes<[]>;
41+
def Write_C: SchedWriteRes<[]>;
4242
def : InstRW<[Write_C], (instrs Inst_A, Inst_B)>;
4343
}
4444

llvm/tools/llvm-exegesis/lib/Analysis.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,9 @@ void Analysis::printInstructionRowCsv(const size_t PointId,
137137
std::tie(SchedClassId, std::ignore) = ResolvedSchedClass::resolveSchedClassId(
138138
State_.getSubtargetInfo(), State_.getInstrInfo(), MCI);
139139
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
140-
const MCSchedClassDesc *const SCDesc =
141-
State_.getSubtargetInfo().getSchedModel().getSchedClassDesc(SchedClassId);
142-
writeEscaped<kEscapeCsv>(OS, SCDesc->Name);
140+
StringRef SCDescName =
141+
State_.getSubtargetInfo().getSchedModel().getSchedClassName(SchedClassId);
142+
writeEscaped<kEscapeCsv>(OS, SCDescName);
143143
#else
144144
OS << SchedClassId;
145145
#endif
@@ -563,7 +563,8 @@ Error Analysis::run<Analysis::PrintSchedClassInconsistencies>(
563563
OS << "<div class=\"inconsistency\"><p>Sched Class <span "
564564
"class=\"sched-class-name\">";
565565
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
566-
writeEscaped<kEscapeHtml>(OS, RSCAndPoints.RSC.SCDesc->Name);
566+
writeEscaped<kEscapeHtml>(OS, SI.getSchedModel().getSchedClassName(
567+
RSCAndPoints.RSC.SchedClassId));
567568
#else
568569
OS << RSCAndPoints.RSC.SchedClassId;
569570
#endif

llvm/utils/TableGen/SubtargetEmitter.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#include "llvm/Support/Format.h"
2828
#include "llvm/Support/raw_ostream.h"
2929
#include "llvm/TableGen/Error.h"
30+
#include "llvm/TableGen/Record.h"
31+
#include "llvm/TableGen/StringToOffsetTable.h"
3032
#include "llvm/TableGen/TableGenBackend.h"
3133
#include <algorithm>
3234
#include <cassert>
@@ -1380,6 +1382,10 @@ void SubtargetEmitter::emitSchedClassTables(SchedClassTables &SchedTables,
13801382
}
13811383
OS << "}; // " << Target << "ReadAdvanceTable\n";
13821384

1385+
// Pool all SchedClass names in a string table.
1386+
StringToOffsetTable StrTab;
1387+
unsigned InvalidNameOff = StrTab.GetOrAddStringOffset("InvalidSchedClass");
1388+
13831389
// Emit a SchedClass table for each processor.
13841390
for (const auto &[Idx, Proc] : enumerate(SchedModels.procModels())) {
13851391
if (!Proc.hasInstrSchedModel())
@@ -1397,14 +1403,15 @@ void SubtargetEmitter::emitSchedClassTables(SchedClassTables &SchedTables,
13971403
// name and position.
13981404
assert(SchedModels.getSchedClass(0).Name == "NoInstrModel" &&
13991405
"invalid class not first");
1400-
OS << " {DBGFIELD(\"InvalidSchedClass\") "
1406+
OS << " {DBGFIELD(" << InvalidNameOff << ") "
14011407
<< MCSchedClassDesc::InvalidNumMicroOps
14021408
<< ", false, false, false, 0, 0, 0, 0, 0, 0},\n";
14031409

14041410
for (unsigned SCIdx = 1, SCEnd = SCTab.size(); SCIdx != SCEnd; ++SCIdx) {
14051411
MCSchedClassDesc &MCDesc = SCTab[SCIdx];
14061412
const CodeGenSchedClass &SchedClass = SchedModels.getSchedClass(SCIdx);
1407-
OS << " {DBGFIELD(\"" << SchedClass.Name << "\") ";
1413+
unsigned NameOff = StrTab.GetOrAddStringOffset(SchedClass.Name);
1414+
OS << " {DBGFIELD(/*" << SchedClass.Name << "*/ " << NameOff << ") ";
14081415
if (SchedClass.Name.size() < 18)
14091416
OS.indent(18 - SchedClass.Name.size());
14101417
OS << MCDesc.NumMicroOps << ", " << (MCDesc.BeginGroup ? "true" : "false")
@@ -1419,6 +1426,8 @@ void SubtargetEmitter::emitSchedClassTables(SchedClassTables &SchedTables,
14191426
}
14201427
OS << "}; // " << Proc.ModelName << "SchedClasses\n";
14211428
}
1429+
1430+
StrTab.EmitStringTableDef(OS, Target + "SchedClassNames");
14221431
}
14231432

14241433
void SubtargetEmitter::emitProcessorModels(raw_ostream &OS) {
@@ -1472,6 +1481,8 @@ void SubtargetEmitter::emitProcessorModels(raw_ostream &OS) {
14721481
else
14731482
OS << " nullptr, nullptr, 0, 0,"
14741483
<< " // No instruction-level machine model.\n";
1484+
OS << " DBGVAL_OR_NULLPTR(&" << Target
1485+
<< "SchedClassNames), // SchedClassNames\n";
14751486
if (PM.hasItineraries())
14761487
OS << " " << PM.ItinsDef->getName() << ",\n";
14771488
else
@@ -1493,8 +1504,10 @@ void SubtargetEmitter::emitSchedModel(raw_ostream &OS) {
14931504
<< "#endif\n"
14941505
<< "#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)\n"
14951506
<< "#define DBGFIELD(x) x,\n"
1507+
<< "#define DBGVAL_OR_NULLPTR(x) x\n"
14961508
<< "#else\n"
14971509
<< "#define DBGFIELD(x)\n"
1510+
<< "#define DBGVAL_OR_NULLPTR(x) nullptr\n"
14981511
<< "#endif\n";
14991512

15001513
if (SchedModels.hasItineraries()) {
@@ -1512,10 +1525,11 @@ void SubtargetEmitter::emitSchedModel(raw_ostream &OS) {
15121525
}
15131526
emitSchedClassTables(SchedTables, OS);
15141527

1515-
OS << "\n#undef DBGFIELD\n";
1516-
15171528
// Emit the processor machine model
15181529
emitProcessorModels(OS);
1530+
1531+
OS << "\n#undef DBGFIELD\n";
1532+
OS << "\n#undef DBGVAL_OR_NULLPTR\n";
15191533
}
15201534

15211535
static void emitPredicateProlog(const RecordKeeper &Records, raw_ostream &OS) {

0 commit comments

Comments
 (0)