forked from OSchip/llvm-project
[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
This commit is contained in:
parent
c23da666b5
commit
d695d0d6f6
|
@ -124,6 +124,7 @@ struct Configuration {
|
||||||
uint32_t dylibCompatibilityVersion = 0;
|
uint32_t dylibCompatibilityVersion = 0;
|
||||||
uint32_t dylibCurrentVersion = 0;
|
uint32_t dylibCurrentVersion = 0;
|
||||||
uint32_t timeTraceGranularity = 500;
|
uint32_t timeTraceGranularity = 500;
|
||||||
|
unsigned optimize;
|
||||||
std::string progName;
|
std::string progName;
|
||||||
|
|
||||||
// For `clang -arch arm64 -arch x86_64`, clang will:
|
// For `clang -arch arm64 -arch x86_64`, clang will:
|
||||||
|
|
|
@ -1170,6 +1170,7 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
|
||||||
symtab->addDynamicLookup(arg->getValue());
|
symtab->addDynamicLookup(arg->getValue());
|
||||||
|
|
||||||
config->mapFile = args.getLastArgValue(OPT_map);
|
config->mapFile = args.getLastArgValue(OPT_map);
|
||||||
|
config->optimize = args::getInteger(args, OPT_O, 1);
|
||||||
config->outputFile = args.getLastArgValue(OPT_o, "a.out");
|
config->outputFile = args.getLastArgValue(OPT_o, "a.out");
|
||||||
config->finalOutput =
|
config->finalOutput =
|
||||||
args.getLastArgValue(OPT_final_output, config->outputFile);
|
args.getLastArgValue(OPT_final_output, config->outputFile);
|
||||||
|
|
|
@ -71,6 +71,8 @@ def lto_O: Joined<["--"], "lto-O">,
|
||||||
def thinlto_cache_policy: Joined<["--"], "thinlto-cache-policy=">,
|
def thinlto_cache_policy: Joined<["--"], "thinlto-cache-policy=">,
|
||||||
HelpText<"Pruning policy for the ThinLTO cache">,
|
HelpText<"Pruning policy for the ThinLTO cache">,
|
||||||
Group<grp_lld>;
|
Group<grp_lld>;
|
||||||
|
def O : JoinedOrSeparate<["-"], "O">,
|
||||||
|
HelpText<"Optimize output file size">;
|
||||||
|
|
||||||
// This is a complete Options.td compiled from Apple's ld(1) manpage
|
// This is a complete Options.td compiled from Apple's ld(1) manpage
|
||||||
// dated 2018-03-07 and cross checked with ld64 source code in repo
|
// dated 2018-03-07 and cross checked with ld64 source code in repo
|
||||||
|
|
|
@ -282,6 +282,7 @@ struct BindIR {
|
||||||
// scream instead of accidentally writing "valid" values.
|
// scream instead of accidentally writing "valid" values.
|
||||||
uint8_t opcode = 0xF0;
|
uint8_t opcode = 0xF0;
|
||||||
uint64_t data = 0;
|
uint64_t data = 0;
|
||||||
|
uint64_t consecutiveCount = 0;
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -297,46 +298,77 @@ static void encodeBinding(const OutputSection *osec, uint64_t outSecOff,
|
||||||
OutputSegment *seg = osec->parent;
|
OutputSegment *seg = osec->parent;
|
||||||
uint64_t offset = osec->getSegmentOffset() + outSecOff;
|
uint64_t offset = osec->getSegmentOffset() + outSecOff;
|
||||||
if (lastBinding.segment != seg) {
|
if (lastBinding.segment != seg) {
|
||||||
BindIR op = {
|
opcodes.push_back(
|
||||||
static_cast<uint8_t>(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB |
|
{static_cast<uint8_t>(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB |
|
||||||
seg->index), // opcode
|
seg->index),
|
||||||
offset // data
|
offset});
|
||||||
};
|
|
||||||
opcodes.push_back(op);
|
|
||||||
lastBinding.segment = seg;
|
lastBinding.segment = seg;
|
||||||
lastBinding.offset = offset;
|
lastBinding.offset = offset;
|
||||||
} else if (lastBinding.offset != offset) {
|
} else if (lastBinding.offset != offset) {
|
||||||
BindIR op = {
|
opcodes.push_back({BIND_OPCODE_ADD_ADDR_ULEB, offset - lastBinding.offset});
|
||||||
static_cast<uint8_t>(BIND_OPCODE_ADD_ADDR_ULEB), // opcode
|
|
||||||
offset - lastBinding.offset // data
|
|
||||||
};
|
|
||||||
opcodes.push_back(op);
|
|
||||||
lastBinding.offset = offset;
|
lastBinding.offset = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastBinding.addend != addend) {
|
if (lastBinding.addend != addend) {
|
||||||
BindIR op = {
|
opcodes.push_back(
|
||||||
static_cast<uint8_t>(BIND_OPCODE_SET_ADDEND_SLEB), // opcode
|
{BIND_OPCODE_SET_ADDEND_SLEB, static_cast<uint64_t>(addend)});
|
||||||
static_cast<uint64_t>(addend) // data
|
|
||||||
};
|
|
||||||
opcodes.push_back(op);
|
|
||||||
lastBinding.addend = addend;
|
lastBinding.addend = addend;
|
||||||
}
|
}
|
||||||
|
|
||||||
BindIR op = {
|
opcodes.push_back({BIND_OPCODE_DO_BIND, 0});
|
||||||
static_cast<uint8_t>(BIND_OPCODE_DO_BIND), // opcode
|
|
||||||
0 // data
|
|
||||||
};
|
|
||||||
opcodes.push_back(op);
|
|
||||||
// DO_BIND causes dyld to both perform the binding and increment the offset
|
// DO_BIND causes dyld to both perform the binding and increment the offset
|
||||||
lastBinding.offset += target->wordSize;
|
lastBinding.offset += target->wordSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void optimizeOpcodes(std::vector<BindIR> &opcodes) {
|
||||||
|
// Pass 1: Combine bind/add pairs
|
||||||
|
size_t i;
|
||||||
|
int pWrite = 0;
|
||||||
|
for (i = 1; i < opcodes.size(); ++i, ++pWrite) {
|
||||||
|
if ((opcodes[i].opcode == BIND_OPCODE_ADD_ADDR_ULEB) &&
|
||||||
|
(opcodes[i - 1].opcode == BIND_OPCODE_DO_BIND)) {
|
||||||
|
opcodes[pWrite].opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB;
|
||||||
|
opcodes[pWrite].data = opcodes[i].data;
|
||||||
|
++i;
|
||||||
|
} else {
|
||||||
|
opcodes[pWrite] = opcodes[i - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == opcodes.size())
|
||||||
|
opcodes[pWrite] = opcodes[i - 1];
|
||||||
|
opcodes.resize(pWrite + 1);
|
||||||
|
|
||||||
|
// Pass 2: Compress two or more bind_add opcodes
|
||||||
|
pWrite = 0;
|
||||||
|
for (i = 1; i < opcodes.size(); ++i, ++pWrite) {
|
||||||
|
if ((opcodes[i].opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) &&
|
||||||
|
(opcodes[i - 1].opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) &&
|
||||||
|
(opcodes[i].data == opcodes[i - 1].data)) {
|
||||||
|
opcodes[pWrite].opcode = BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB;
|
||||||
|
opcodes[pWrite].consecutiveCount = 2;
|
||||||
|
opcodes[pWrite].data = opcodes[i].data;
|
||||||
|
++i;
|
||||||
|
while (i < opcodes.size() &&
|
||||||
|
(opcodes[i].opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) &&
|
||||||
|
(opcodes[i].data == opcodes[i - 1].data)) {
|
||||||
|
opcodes[pWrite].consecutiveCount++;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
opcodes[pWrite] = opcodes[i - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == opcodes.size())
|
||||||
|
opcodes[pWrite] = opcodes[i - 1];
|
||||||
|
opcodes.resize(pWrite + 1);
|
||||||
|
}
|
||||||
|
|
||||||
static void flushOpcodes(const BindIR &op, raw_svector_ostream &os) {
|
static void flushOpcodes(const BindIR &op, raw_svector_ostream &os) {
|
||||||
uint8_t opcode = op.opcode & BIND_OPCODE_MASK;
|
uint8_t opcode = op.opcode & BIND_OPCODE_MASK;
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
|
case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
|
||||||
case BIND_OPCODE_ADD_ADDR_ULEB:
|
case BIND_OPCODE_ADD_ADDR_ULEB:
|
||||||
|
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
|
||||||
os << op.opcode;
|
os << op.opcode;
|
||||||
encodeULEB128(op.data, os);
|
encodeULEB128(op.data, os);
|
||||||
break;
|
break;
|
||||||
|
@ -347,6 +379,11 @@ static void flushOpcodes(const BindIR &op, raw_svector_ostream &os) {
|
||||||
case BIND_OPCODE_DO_BIND:
|
case BIND_OPCODE_DO_BIND:
|
||||||
os << op.opcode;
|
os << op.opcode;
|
||||||
break;
|
break;
|
||||||
|
case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
|
||||||
|
os << op.opcode;
|
||||||
|
encodeULEB128(op.consecutiveCount, os);
|
||||||
|
encodeULEB128(op.data, os);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
llvm_unreachable("cannot bind to an unrecognized symbol");
|
llvm_unreachable("cannot bind to an unrecognized symbol");
|
||||||
}
|
}
|
||||||
|
@ -446,6 +483,8 @@ void BindingSection::finalizeContents() {
|
||||||
encodeBinding(b.target.isec->parent,
|
encodeBinding(b.target.isec->parent,
|
||||||
b.target.isec->getOffset(b.target.offset), b.addend,
|
b.target.isec->getOffset(b.target.offset), b.addend,
|
||||||
lastBinding, opcodes);
|
lastBinding, opcodes);
|
||||||
|
if (config->optimize > 1)
|
||||||
|
optimizeOpcodes(opcodes);
|
||||||
for (const auto &op : opcodes)
|
for (const auto &op : opcodes)
|
||||||
flushOpcodes(op, os);
|
flushOpcodes(op, os);
|
||||||
}
|
}
|
||||||
|
@ -478,6 +517,8 @@ void WeakBindingSection::finalizeContents() {
|
||||||
encodeBinding(b.target.isec->parent,
|
encodeBinding(b.target.isec->parent,
|
||||||
b.target.isec->getOffset(b.target.offset), b.addend,
|
b.target.isec->getOffset(b.target.offset), b.addend,
|
||||||
lastBinding, opcodes);
|
lastBinding, opcodes);
|
||||||
|
if (config->optimize > 1)
|
||||||
|
optimizeOpcodes(opcodes);
|
||||||
for (const auto &op : opcodes)
|
for (const auto &op : opcodes)
|
||||||
flushOpcodes(op, os);
|
flushOpcodes(op, os);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,28 +2,83 @@
|
||||||
# RUN: rm -rf %t; split-file %s %t
|
# RUN: rm -rf %t; split-file %s %t
|
||||||
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/foo.s -o %t/foo.o
|
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/foo.s -o %t/foo.o
|
||||||
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/test.s -o %t/test.o
|
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/test.s -o %t/test.o
|
||||||
# RUN: %lld -dylib %t/foo.o -o %t/libfoo.dylib
|
# RUN: %lld -O2 -dylib %t/foo.o -o %t/libfoo.dylib
|
||||||
# RUN: %lld -lSystem %t/test.o %t/libfoo.dylib -o %t/test
|
# RUN: %lld -O2 -lSystem %t/test.o %t/libfoo.dylib -o %t/test
|
||||||
|
|
||||||
## Make sure we emit exactly one BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM per
|
## Test:
|
||||||
## symbol.
|
## 1/ We emit exactly one BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM per symbol.
|
||||||
# RUN: obj2yaml %t/test | FileCheck %s --implicit-check-not BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
|
## 2/ Combine BIND_OPCODE_DO_BIND and BIND_OPCODE_ADD_ADDR_ULEB pairs.
|
||||||
|
## 3/ Compact BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
|
||||||
|
# RUN: obj2yaml %t/test | FileCheck %s
|
||||||
|
|
||||||
# CHECK: Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
|
# CHECK: BindOpcodes:
|
||||||
# CHECK-NEXT: Imm: 0
|
# CHECK-NEXT: Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
|
||||||
# CHECK-NEXT: Symbol: _foo
|
# CHECK-NEXT: Imm: 0
|
||||||
|
# CHECK-NEXT: Symbol: _foo
|
||||||
# CHECK: Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
|
# CHECK-NEXT: Opcode: BIND_OPCODE_SET_TYPE_IMM
|
||||||
# CHECK-NEXT: Imm: 0
|
# CHECK-NEXT: Imm: 1
|
||||||
# CHECK-NEXT: Symbol: _bar
|
# CHECK-NEXT: Symbol: ''
|
||||||
|
# CHECK-NEXT: Opcode: BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
|
||||||
|
# CHECK-NEXT: Imm: 2
|
||||||
|
# CHECK-NEXT: Symbol: ''
|
||||||
|
# CHECK-NEXT: Opcode: BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
|
||||||
|
# CHECK-NEXT: Imm: 2
|
||||||
|
# CHECK-NEXT: ULEBExtraData: [ 0x0 ]
|
||||||
|
# CHECK-NEXT: Symbol: ''
|
||||||
|
# CHECK-NEXT: Opcode: BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB
|
||||||
|
# CHECK-NEXT: Imm: 0
|
||||||
|
# CHECK-NEXT: ULEBExtraData: [ 0x2, 0x8 ]
|
||||||
|
# CHECK-NEXT: Symbol: ''
|
||||||
|
# CHECK-NEXT: Opcode: BIND_OPCODE_SET_ADDEND_SLEB
|
||||||
|
# CHECK-NEXT: Imm: 0
|
||||||
|
# CHECK-NEXT: SLEBExtraData: [ 1 ]
|
||||||
|
# CHECK-NEXT: Symbol: ''
|
||||||
|
# CHECK-NEXT: Opcode: BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
|
||||||
|
# CHECK-NEXT: Imm: 0
|
||||||
|
# CHECK-NEXT: ULEBExtraData: [ 0x1008 ]
|
||||||
|
# CHECK-NEXT: Symbol: ''
|
||||||
|
# CHECK-NEXT: Opcode: BIND_OPCODE_SET_ADDEND_SLEB
|
||||||
|
# CHECK-NEXT: Imm: 0
|
||||||
|
# CHECK-NEXT: SLEBExtraData: [ 0 ]
|
||||||
|
# CHECK-NEXT: Symbol: ''
|
||||||
|
# CHECK-NEXT: Opcode: BIND_OPCODE_DO_BIND
|
||||||
|
# CHECK-NEXT: Imm: 0
|
||||||
|
# CHECK-NEXT: Symbol: ''
|
||||||
|
# CHECK-NEXT: Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
|
||||||
|
# CHECK-NEXT: Imm: 0
|
||||||
|
# CHECK-NEXT: Symbol: _bar
|
||||||
|
# CHECK-NEXT: Opcode: BIND_OPCODE_SET_TYPE_IMM
|
||||||
|
# CHECK-NEXT: Imm: 1
|
||||||
|
# CHECK-NEXT: Symbol: ''
|
||||||
|
# CHECK-NEXT: Opcode: BIND_OPCODE_ADD_ADDR_ULEB
|
||||||
|
# CHECK-NEXT: Imm: 0
|
||||||
|
# CHECK-NEXT: ULEBExtraData: [ 0xFFFFFFFFFFFFEFD0 ]
|
||||||
|
# CHECK-NEXT: Symbol: ''
|
||||||
|
# CHECK-NEXT: Opcode: BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
|
||||||
|
# CHECK-NEXT: Imm: 0
|
||||||
|
# CHECK-NEXT: ULEBExtraData: [ 0x8 ]
|
||||||
|
# CHECK-NEXT: Symbol: ''
|
||||||
|
# CHECK-NEXT: Opcode: BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
|
||||||
|
# CHECK-NEXT: Imm: 0
|
||||||
|
# CHECK-NEXT: ULEBExtraData: [ 0x1008 ]
|
||||||
|
# CHECK-NEXT: Symbol: ''
|
||||||
|
# CHECK-NEXT: Opcode: BIND_OPCODE_DO_BIND
|
||||||
|
# CHECK-NEXT: Imm: 0
|
||||||
|
# CHECK-NEXT: Symbol: ''
|
||||||
|
# CHECK-NEXT: Opcode: BIND_OPCODE_DONE
|
||||||
|
# CHECK-NEXT: Imm: 0
|
||||||
|
# CHECK-NEXT: Symbol: ''
|
||||||
|
|
||||||
# RUN: llvm-objdump --macho --bind %t/test | FileCheck %s --check-prefix=BIND
|
# RUN: llvm-objdump --macho --bind %t/test | FileCheck %s --check-prefix=BIND
|
||||||
# BIND: Bind table:
|
# BIND: Bind table:
|
||||||
# BIND-NEXT: segment section address type addend dylib symbol
|
# BIND-NEXT: segment section address type addend dylib symbol
|
||||||
# BIND-NEXT: __DATA __data {{.*}} pointer 0 libfoo _foo
|
# BIND-NEXT: __DATA __data 0x100001000 pointer 0 libfoo _foo
|
||||||
# BIND-NEXT: __DATA __data {{.*}} pointer 0 libfoo _foo
|
# BIND-NEXT: __DATA __data 0x100001010 pointer 0 libfoo _foo
|
||||||
# BIND-NEXT: __DATA __data {{.*}} pointer 0 libfoo _bar
|
# BIND-NEXT: __DATA __data 0x100001020 pointer 1 libfoo _foo
|
||||||
# BIND-NEXT: __DATA __data {{.*}} pointer 0 libfoo _bar
|
# BIND-NEXT: __DATA __data 0x100002030 pointer 0 libfoo _foo
|
||||||
|
# BIND-NEXT: __DATA __data 0x100001008 pointer 0 libfoo _bar
|
||||||
|
# BIND-NEXT: __DATA __data 0x100001018 pointer 0 libfoo _bar
|
||||||
|
# BIND-NEXT: __DATA __data 0x100002028 pointer 0 libfoo _bar
|
||||||
# BIND-EMPTY:
|
# BIND-EMPTY:
|
||||||
|
|
||||||
#--- foo.s
|
#--- foo.s
|
||||||
|
@ -39,6 +94,10 @@ _bar:
|
||||||
.quad _bar
|
.quad _bar
|
||||||
.quad _foo
|
.quad _foo
|
||||||
.quad _bar
|
.quad _bar
|
||||||
|
.quad _foo+1
|
||||||
|
.zero 0x1000
|
||||||
|
.quad _bar
|
||||||
|
.quad _foo
|
||||||
|
|
||||||
.globl _main
|
.globl _main
|
||||||
.text
|
.text
|
||||||
|
|
Loading…
Reference in New Issue