[lld] d695d0d - [lld-macho] Optimize bind opcodes with multiple passes

Vincent Lee via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 15 20:53:03 PDT 2021


Author: Vincent Lee
Date: 2021-07-15T20:52:46-07:00
New Revision: d695d0d6f60528841ab8671cdccff85f889ca95d

URL: https://github.com/llvm/llvm-project/commit/d695d0d6f60528841ab8671cdccff85f889ca95d
DIFF: https://github.com/llvm/llvm-project/commit/d695d0d6f60528841ab8671cdccff85f889ca95d.diff

LOG: [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

Added: 
    

Modified: 
    lld/MachO/Config.h
    lld/MachO/Driver.cpp
    lld/MachO/Options.td
    lld/MachO/SyntheticSections.cpp
    lld/test/MachO/bind-opcodes.s

Removed: 
    


################################################################################
diff  --git a/lld/MachO/Config.h b/lld/MachO/Config.h
index fb111901dd96..d8bff044187c 100644
--- a/lld/MachO/Config.h
+++ b/lld/MachO/Config.h
@@ -124,6 +124,7 @@ struct Configuration {
   uint32_t dylibCompatibilityVersion = 0;
   uint32_t dylibCurrentVersion = 0;
   uint32_t timeTraceGranularity = 500;
+  unsigned optimize;
   std::string progName;
 
   // For `clang -arch arm64 -arch x86_64`, clang will:

diff  --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp
index 3150fa5e2e41..feb080346cb3 100644
--- a/lld/MachO/Driver.cpp
+++ b/lld/MachO/Driver.cpp
@@ -1170,6 +1170,7 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
     symtab->addDynamicLookup(arg->getValue());
 
   config->mapFile = args.getLastArgValue(OPT_map);
+  config->optimize = args::getInteger(args, OPT_O, 1);
   config->outputFile = args.getLastArgValue(OPT_o, "a.out");
   config->finalOutput =
       args.getLastArgValue(OPT_final_output, config->outputFile);

diff  --git a/lld/MachO/Options.td b/lld/MachO/Options.td
index 7ac2d9834f2f..cda857d605bd 100644
--- a/lld/MachO/Options.td
+++ b/lld/MachO/Options.td
@@ -71,6 +71,8 @@ def lto_O: Joined<["--"], "lto-O">,
 def thinlto_cache_policy: Joined<["--"], "thinlto-cache-policy=">,
     HelpText<"Pruning policy for the ThinLTO cache">,
     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
 // dated 2018-03-07 and cross checked with ld64 source code in repo

diff  --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp
index 4b8d3149f9e7..aaed5b858644 100644
--- a/lld/MachO/SyntheticSections.cpp
+++ b/lld/MachO/SyntheticSections.cpp
@@ -282,6 +282,7 @@ struct BindIR {
   // scream instead of accidentally writing "valid" values.
   uint8_t opcode = 0xF0;
   uint64_t data = 0;
+  uint64_t consecutiveCount = 0;
 };
 } // namespace
 
@@ -297,46 +298,77 @@ static void encodeBinding(const OutputSection *osec, uint64_t outSecOff,
   OutputSegment *seg = osec->parent;
   uint64_t offset = osec->getSegmentOffset() + outSecOff;
   if (lastBinding.segment != seg) {
-    BindIR op = {
-        static_cast<uint8_t>(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB |
-                             seg->index), // opcode
-        offset                            // data
-    };
-    opcodes.push_back(op);
+    opcodes.push_back(
+        {static_cast<uint8_t>(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB |
+                              seg->index),
+         offset});
     lastBinding.segment = seg;
     lastBinding.offset = offset;
   } else if (lastBinding.offset != offset) {
-    BindIR op = {
-        static_cast<uint8_t>(BIND_OPCODE_ADD_ADDR_ULEB), // opcode
-        offset - lastBinding.offset                      // data
-    };
-    opcodes.push_back(op);
+    opcodes.push_back({BIND_OPCODE_ADD_ADDR_ULEB, offset - lastBinding.offset});
     lastBinding.offset = offset;
   }
 
   if (lastBinding.addend != addend) {
-    BindIR op = {
-        static_cast<uint8_t>(BIND_OPCODE_SET_ADDEND_SLEB), // opcode
-        static_cast<uint64_t>(addend)                      // data
-    };
-    opcodes.push_back(op);
+    opcodes.push_back(
+        {BIND_OPCODE_SET_ADDEND_SLEB, static_cast<uint64_t>(addend)});
     lastBinding.addend = addend;
   }
 
-  BindIR op = {
-      static_cast<uint8_t>(BIND_OPCODE_DO_BIND), // opcode
-      0                                          // data
-  };
-  opcodes.push_back(op);
+  opcodes.push_back({BIND_OPCODE_DO_BIND, 0});
   // DO_BIND causes dyld to both perform the binding and increment the offset
   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) {
   uint8_t opcode = op.opcode & BIND_OPCODE_MASK;
   switch (opcode) {
   case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
   case BIND_OPCODE_ADD_ADDR_ULEB:
+  case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
     os << op.opcode;
     encodeULEB128(op.data, os);
     break;
@@ -347,6 +379,11 @@ static void flushOpcodes(const BindIR &op, raw_svector_ostream &os) {
   case BIND_OPCODE_DO_BIND:
     os << op.opcode;
     break;
+  case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+    os << op.opcode;
+    encodeULEB128(op.consecutiveCount, os);
+    encodeULEB128(op.data, os);
+    break;
   default:
     llvm_unreachable("cannot bind to an unrecognized symbol");
   }
@@ -446,6 +483,8 @@ void BindingSection::finalizeContents() {
       encodeBinding(b.target.isec->parent,
                     b.target.isec->getOffset(b.target.offset), b.addend,
                     lastBinding, opcodes);
+    if (config->optimize > 1)
+      optimizeOpcodes(opcodes);
     for (const auto &op : opcodes)
       flushOpcodes(op, os);
   }
@@ -478,6 +517,8 @@ void WeakBindingSection::finalizeContents() {
       encodeBinding(b.target.isec->parent,
                     b.target.isec->getOffset(b.target.offset), b.addend,
                     lastBinding, opcodes);
+    if (config->optimize > 1)
+      optimizeOpcodes(opcodes);
     for (const auto &op : opcodes)
       flushOpcodes(op, os);
   }

diff  --git a/lld/test/MachO/bind-opcodes.s b/lld/test/MachO/bind-opcodes.s
index b66bffbd8c41..b313afbef8e6 100644
--- a/lld/test/MachO/bind-opcodes.s
+++ b/lld/test/MachO/bind-opcodes.s
@@ -2,28 +2,83 @@
 # 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/test.s -o %t/test.o
-# RUN: %lld -dylib %t/foo.o -o %t/libfoo.dylib
-# RUN: %lld -lSystem %t/test.o %t/libfoo.dylib -o %t/test
+# RUN: %lld -O2 -dylib %t/foo.o -o %t/libfoo.dylib
+# 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
-## symbol.
-# RUN: obj2yaml %t/test | FileCheck %s --implicit-check-not BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
+## Test:
+## 1/ We emit exactly one BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM per symbol.
+## 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-NEXT: Imm:             0
-# CHECK-NEXT: Symbol:          _foo
-
-# CHECK:      Opcode:          BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
-# CHECK-NEXT: Imm:             0
-# CHECK-NEXT: Symbol:          _bar
+# CHECK:      BindOpcodes:
+# CHECK-NEXT:   Opcode:          BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
+# CHECK-NEXT:   Imm:             0
+# CHECK-NEXT:   Symbol:          _foo
+# CHECK-NEXT:   Opcode:          BIND_OPCODE_SET_TYPE_IMM
+# CHECK-NEXT:   Imm:             1
+# 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
 # BIND:       Bind table:
-# BIND-NEXT:  segment  section address type    addend dylib   symbol
-# BIND-NEXT:  __DATA   __data  {{.*}}  pointer      0 libfoo  _foo
-# BIND-NEXT:  __DATA   __data  {{.*}}  pointer      0 libfoo  _foo
-# BIND-NEXT:  __DATA   __data  {{.*}}  pointer      0 libfoo  _bar
-# BIND-NEXT:  __DATA   __data  {{.*}}  pointer      0 libfoo  _bar
+# BIND-NEXT:  segment  section   address      type       addend dylib     symbol
+# BIND-NEXT:  __DATA   __data    0x100001000  pointer         0 libfoo    _foo
+# BIND-NEXT:  __DATA   __data    0x100001010  pointer         0 libfoo    _foo
+# BIND-NEXT:  __DATA   __data    0x100001020  pointer         1 libfoo    _foo
+# 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:
 
 #--- foo.s
@@ -39,6 +94,10 @@ _bar:
 .quad _bar
 .quad _foo
 .quad _bar
+.quad _foo+1
+.zero 0x1000
+.quad _bar
+.quad _foo
 
 .globl _main
 .text


        


More information about the llvm-commits mailing list