Skip to content

Commit d695d0d

Browse files
committed
[lld-macho] Optimize bind opcodes with multiple passes
In D105866, we used an intermediate container to store a list of opcodes. Here, we use that data structure to help us perform optimization passes that would allow a more efficient encoding of bind opcodes. Currently, the functionality mirrors the optimization pass {1,2} done in ld64 for bind opcodes under optimization gate to prevent slight regressions. Reviewed By: int3, #lld-macho Differential Revision: https://reviews.llvm.org/D105867
1 parent c23da66 commit d695d0d

File tree

5 files changed

+142
-38
lines changed

5 files changed

+142
-38
lines changed

lld/MachO/Config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ struct Configuration {
124124
uint32_t dylibCompatibilityVersion = 0;
125125
uint32_t dylibCurrentVersion = 0;
126126
uint32_t timeTraceGranularity = 500;
127+
unsigned optimize;
127128
std::string progName;
128129

129130
// For `clang -arch arm64 -arch x86_64`, clang will:

lld/MachO/Driver.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,6 +1170,7 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
11701170
symtab->addDynamicLookup(arg->getValue());
11711171

11721172
config->mapFile = args.getLastArgValue(OPT_map);
1173+
config->optimize = args::getInteger(args, OPT_O, 1);
11731174
config->outputFile = args.getLastArgValue(OPT_o, "a.out");
11741175
config->finalOutput =
11751176
args.getLastArgValue(OPT_final_output, config->outputFile);

lld/MachO/Options.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ def lto_O: Joined<["--"], "lto-O">,
7171
def thinlto_cache_policy: Joined<["--"], "thinlto-cache-policy=">,
7272
HelpText<"Pruning policy for the ThinLTO cache">,
7373
Group<grp_lld>;
74+
def O : JoinedOrSeparate<["-"], "O">,
75+
HelpText<"Optimize output file size">;
7476

7577
// This is a complete Options.td compiled from Apple's ld(1) manpage
7678
// dated 2018-03-07 and cross checked with ld64 source code in repo

lld/MachO/SyntheticSections.cpp

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ struct BindIR {
282282
// scream instead of accidentally writing "valid" values.
283283
uint8_t opcode = 0xF0;
284284
uint64_t data = 0;
285+
uint64_t consecutiveCount = 0;
285286
};
286287
} // namespace
287288

@@ -297,46 +298,77 @@ static void encodeBinding(const OutputSection *osec, uint64_t outSecOff,
297298
OutputSegment *seg = osec->parent;
298299
uint64_t offset = osec->getSegmentOffset() + outSecOff;
299300
if (lastBinding.segment != seg) {
300-
BindIR op = {
301-
static_cast<uint8_t>(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB |
302-
seg->index), // opcode
303-
offset // data
304-
};
305-
opcodes.push_back(op);
301+
opcodes.push_back(
302+
{static_cast<uint8_t>(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB |
303+
seg->index),
304+
offset});
306305
lastBinding.segment = seg;
307306
lastBinding.offset = offset;
308307
} else if (lastBinding.offset != offset) {
309-
BindIR op = {
310-
static_cast<uint8_t>(BIND_OPCODE_ADD_ADDR_ULEB), // opcode
311-
offset - lastBinding.offset // data
312-
};
313-
opcodes.push_back(op);
308+
opcodes.push_back({BIND_OPCODE_ADD_ADDR_ULEB, offset - lastBinding.offset});
314309
lastBinding.offset = offset;
315310
}
316311

317312
if (lastBinding.addend != addend) {
318-
BindIR op = {
319-
static_cast<uint8_t>(BIND_OPCODE_SET_ADDEND_SLEB), // opcode
320-
static_cast<uint64_t>(addend) // data
321-
};
322-
opcodes.push_back(op);
313+
opcodes.push_back(
314+
{BIND_OPCODE_SET_ADDEND_SLEB, static_cast<uint64_t>(addend)});
323315
lastBinding.addend = addend;
324316
}
325317

326-
BindIR op = {
327-
static_cast<uint8_t>(BIND_OPCODE_DO_BIND), // opcode
328-
0 // data
329-
};
330-
opcodes.push_back(op);
318+
opcodes.push_back({BIND_OPCODE_DO_BIND, 0});
331319
// DO_BIND causes dyld to both perform the binding and increment the offset
332320
lastBinding.offset += target->wordSize;
333321
}
334322

323+
static void optimizeOpcodes(std::vector<BindIR> &opcodes) {
324+
// Pass 1: Combine bind/add pairs
325+
size_t i;
326+
int pWrite = 0;
327+
for (i = 1; i < opcodes.size(); ++i, ++pWrite) {
328+
if ((opcodes[i].opcode == BIND_OPCODE_ADD_ADDR_ULEB) &&
329+
(opcodes[i - 1].opcode == BIND_OPCODE_DO_BIND)) {
330+
opcodes[pWrite].opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB;
331+
opcodes[pWrite].data = opcodes[i].data;
332+
++i;
333+
} else {
334+
opcodes[pWrite] = opcodes[i - 1];
335+
}
336+
}
337+
if (i == opcodes.size())
338+
opcodes[pWrite] = opcodes[i - 1];
339+
opcodes.resize(pWrite + 1);
340+
341+
// Pass 2: Compress two or more bind_add opcodes
342+
pWrite = 0;
343+
for (i = 1; i < opcodes.size(); ++i, ++pWrite) {
344+
if ((opcodes[i].opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) &&
345+
(opcodes[i - 1].opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) &&
346+
(opcodes[i].data == opcodes[i - 1].data)) {
347+
opcodes[pWrite].opcode = BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB;
348+
opcodes[pWrite].consecutiveCount = 2;
349+
opcodes[pWrite].data = opcodes[i].data;
350+
++i;
351+
while (i < opcodes.size() &&
352+
(opcodes[i].opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) &&
353+
(opcodes[i].data == opcodes[i - 1].data)) {
354+
opcodes[pWrite].consecutiveCount++;
355+
++i;
356+
}
357+
} else {
358+
opcodes[pWrite] = opcodes[i - 1];
359+
}
360+
}
361+
if (i == opcodes.size())
362+
opcodes[pWrite] = opcodes[i - 1];
363+
opcodes.resize(pWrite + 1);
364+
}
365+
335366
static void flushOpcodes(const BindIR &op, raw_svector_ostream &os) {
336367
uint8_t opcode = op.opcode & BIND_OPCODE_MASK;
337368
switch (opcode) {
338369
case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
339370
case BIND_OPCODE_ADD_ADDR_ULEB:
371+
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
340372
os << op.opcode;
341373
encodeULEB128(op.data, os);
342374
break;
@@ -347,6 +379,11 @@ static void flushOpcodes(const BindIR &op, raw_svector_ostream &os) {
347379
case BIND_OPCODE_DO_BIND:
348380
os << op.opcode;
349381
break;
382+
case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
383+
os << op.opcode;
384+
encodeULEB128(op.consecutiveCount, os);
385+
encodeULEB128(op.data, os);
386+
break;
350387
default:
351388
llvm_unreachable("cannot bind to an unrecognized symbol");
352389
}
@@ -446,6 +483,8 @@ void BindingSection::finalizeContents() {
446483
encodeBinding(b.target.isec->parent,
447484
b.target.isec->getOffset(b.target.offset), b.addend,
448485
lastBinding, opcodes);
486+
if (config->optimize > 1)
487+
optimizeOpcodes(opcodes);
449488
for (const auto &op : opcodes)
450489
flushOpcodes(op, os);
451490
}
@@ -478,6 +517,8 @@ void WeakBindingSection::finalizeContents() {
478517
encodeBinding(b.target.isec->parent,
479518
b.target.isec->getOffset(b.target.offset), b.addend,
480519
lastBinding, opcodes);
520+
if (config->optimize > 1)
521+
optimizeOpcodes(opcodes);
481522
for (const auto &op : opcodes)
482523
flushOpcodes(op, os);
483524
}

lld/test/MachO/bind-opcodes.s

Lines changed: 76 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,83 @@
22
# RUN: rm -rf %t; split-file %s %t
33
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/foo.s -o %t/foo.o
44
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/test.s -o %t/test.o
5-
# RUN: %lld -dylib %t/foo.o -o %t/libfoo.dylib
6-
# RUN: %lld -lSystem %t/test.o %t/libfoo.dylib -o %t/test
5+
# RUN: %lld -O2 -dylib %t/foo.o -o %t/libfoo.dylib
6+
# RUN: %lld -O2 -lSystem %t/test.o %t/libfoo.dylib -o %t/test
77

8-
## Make sure we emit exactly one BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM per
9-
## symbol.
10-
# RUN: obj2yaml %t/test | FileCheck %s --implicit-check-not BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
8+
## Test:
9+
## 1/ We emit exactly one BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM per symbol.
10+
## 2/ Combine BIND_OPCODE_DO_BIND and BIND_OPCODE_ADD_ADDR_ULEB pairs.
11+
## 3/ Compact BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
12+
# RUN: obj2yaml %t/test | FileCheck %s
1113

12-
# CHECK: Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
13-
# CHECK-NEXT: Imm: 0
14-
# CHECK-NEXT: Symbol: _foo
15-
16-
# CHECK: Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
17-
# CHECK-NEXT: Imm: 0
18-
# CHECK-NEXT: Symbol: _bar
14+
# CHECK: BindOpcodes:
15+
# CHECK-NEXT: Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
16+
# CHECK-NEXT: Imm: 0
17+
# CHECK-NEXT: Symbol: _foo
18+
# CHECK-NEXT: Opcode: BIND_OPCODE_SET_TYPE_IMM
19+
# CHECK-NEXT: Imm: 1
20+
# CHECK-NEXT: Symbol: ''
21+
# CHECK-NEXT: Opcode: BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
22+
# CHECK-NEXT: Imm: 2
23+
# CHECK-NEXT: Symbol: ''
24+
# CHECK-NEXT: Opcode: BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
25+
# CHECK-NEXT: Imm: 2
26+
# CHECK-NEXT: ULEBExtraData: [ 0x0 ]
27+
# CHECK-NEXT: Symbol: ''
28+
# CHECK-NEXT: Opcode: BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB
29+
# CHECK-NEXT: Imm: 0
30+
# CHECK-NEXT: ULEBExtraData: [ 0x2, 0x8 ]
31+
# CHECK-NEXT: Symbol: ''
32+
# CHECK-NEXT: Opcode: BIND_OPCODE_SET_ADDEND_SLEB
33+
# CHECK-NEXT: Imm: 0
34+
# CHECK-NEXT: SLEBExtraData: [ 1 ]
35+
# CHECK-NEXT: Symbol: ''
36+
# CHECK-NEXT: Opcode: BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
37+
# CHECK-NEXT: Imm: 0
38+
# CHECK-NEXT: ULEBExtraData: [ 0x1008 ]
39+
# CHECK-NEXT: Symbol: ''
40+
# CHECK-NEXT: Opcode: BIND_OPCODE_SET_ADDEND_SLEB
41+
# CHECK-NEXT: Imm: 0
42+
# CHECK-NEXT: SLEBExtraData: [ 0 ]
43+
# CHECK-NEXT: Symbol: ''
44+
# CHECK-NEXT: Opcode: BIND_OPCODE_DO_BIND
45+
# CHECK-NEXT: Imm: 0
46+
# CHECK-NEXT: Symbol: ''
47+
# CHECK-NEXT: Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
48+
# CHECK-NEXT: Imm: 0
49+
# CHECK-NEXT: Symbol: _bar
50+
# CHECK-NEXT: Opcode: BIND_OPCODE_SET_TYPE_IMM
51+
# CHECK-NEXT: Imm: 1
52+
# CHECK-NEXT: Symbol: ''
53+
# CHECK-NEXT: Opcode: BIND_OPCODE_ADD_ADDR_ULEB
54+
# CHECK-NEXT: Imm: 0
55+
# CHECK-NEXT: ULEBExtraData: [ 0xFFFFFFFFFFFFEFD0 ]
56+
# CHECK-NEXT: Symbol: ''
57+
# CHECK-NEXT: Opcode: BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
58+
# CHECK-NEXT: Imm: 0
59+
# CHECK-NEXT: ULEBExtraData: [ 0x8 ]
60+
# CHECK-NEXT: Symbol: ''
61+
# CHECK-NEXT: Opcode: BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
62+
# CHECK-NEXT: Imm: 0
63+
# CHECK-NEXT: ULEBExtraData: [ 0x1008 ]
64+
# CHECK-NEXT: Symbol: ''
65+
# CHECK-NEXT: Opcode: BIND_OPCODE_DO_BIND
66+
# CHECK-NEXT: Imm: 0
67+
# CHECK-NEXT: Symbol: ''
68+
# CHECK-NEXT: Opcode: BIND_OPCODE_DONE
69+
# CHECK-NEXT: Imm: 0
70+
# CHECK-NEXT: Symbol: ''
1971

2072
# RUN: llvm-objdump --macho --bind %t/test | FileCheck %s --check-prefix=BIND
2173
# BIND: Bind table:
22-
# BIND-NEXT: segment section address type addend dylib symbol
23-
# BIND-NEXT: __DATA __data {{.*}} pointer 0 libfoo _foo
24-
# BIND-NEXT: __DATA __data {{.*}} pointer 0 libfoo _foo
25-
# BIND-NEXT: __DATA __data {{.*}} pointer 0 libfoo _bar
26-
# BIND-NEXT: __DATA __data {{.*}} pointer 0 libfoo _bar
74+
# BIND-NEXT: segment section address type addend dylib symbol
75+
# BIND-NEXT: __DATA __data 0x100001000 pointer 0 libfoo _foo
76+
# BIND-NEXT: __DATA __data 0x100001010 pointer 0 libfoo _foo
77+
# BIND-NEXT: __DATA __data 0x100001020 pointer 1 libfoo _foo
78+
# BIND-NEXT: __DATA __data 0x100002030 pointer 0 libfoo _foo
79+
# BIND-NEXT: __DATA __data 0x100001008 pointer 0 libfoo _bar
80+
# BIND-NEXT: __DATA __data 0x100001018 pointer 0 libfoo _bar
81+
# BIND-NEXT: __DATA __data 0x100002028 pointer 0 libfoo _bar
2782
# BIND-EMPTY:
2883

2984
#--- foo.s
@@ -39,6 +94,10 @@ _bar:
3994
.quad _bar
4095
.quad _foo
4196
.quad _bar
97+
.quad _foo+1
98+
.zero 0x1000
99+
.quad _bar
100+
.quad _foo
42101

43102
.globl _main
44103
.text

0 commit comments

Comments
 (0)