[all-commits] [llvm/llvm-project] 04259c: [lld-macho] Implement cstring deduplication

Jez Ng via All-commits all-commits at lists.llvm.org
Mon Jun 7 20:48:55 PDT 2021


  Branch: refs/heads/main
  Home:   https://github.com/llvm/llvm-project
  Commit: 04259cde15a9fad31421e9161f160ee23f84ebdd
      https://github.com/llvm/llvm-project/commit/04259cde15a9fad31421e9161f160ee23f84ebdd
  Author: Jez Ng <jezng at fb.com>
  Date:   2021-06-07 (Mon, 07 Jun 2021)

  Changed paths:
    M lld/MachO/ConcatOutputSection.cpp
    M lld/MachO/ConcatOutputSection.h
    M lld/MachO/Config.h
    M lld/MachO/Driver.cpp
    M lld/MachO/InputFiles.cpp
    M lld/MachO/InputFiles.h
    M lld/MachO/InputSection.cpp
    M lld/MachO/InputSection.h
    M lld/MachO/Options.td
    M lld/MachO/Symbols.cpp
    M lld/MachO/SyntheticSections.cpp
    M lld/MachO/SyntheticSections.h
    M lld/MachO/UnwindInfoSection.cpp
    M lld/MachO/Writer.cpp
    A lld/test/MachO/cstring-dedup.s
    A lld/test/MachO/invalid/cstring-dedup.s
    M lld/test/MachO/invalid/reserved-section-name.s
    M lld/test/MachO/subsections-section-relocs.s
    M lld/test/MachO/x86-64-relocs.s

  Log Message:
  -----------
  [lld-macho] Implement cstring deduplication

Our implementation draws heavily from LLD-ELF's, which in turn delegates
its string deduplication to llvm-mc's StringTableBuilder. The messiness of
this diff is largely due to the fact that we've previously assumed that
all InputSections get concatenated together to form the output. This is
no longer true with CStringInputSections, which split their contents into
StringPieces. StringPieces are much more lightweight than InputSections,
which is important as we create a lot of them. They may also overlap in
the output, which makes it possible for strings to be tail-merged. In
fact, the initial version of this diff implemented tail merging, but
I've dropped it for reasons I'll explain later.

**Alignment Issues**

Mergeable cstring literals are found under the `__TEXT,__cstring`
section. In contrast to ELF, which puts strings that need different
alignments into different sections, clang's Mach-O backend puts them all
in one section. Strings that need to be aligned have the `.p2align`
directive emitted before them, which simply translates into zero padding
in the object file.

I *think* ld64 extracts the desired per-string alignment from this data
by preserving each string's offset from the last section-aligned
address. I'm not entirely certain since it doesn't seem consistent about
doing this; but perhaps this can be chalked up to cases where ld64 has
to deduplicate strings with different offset/alignment combos -- it
seems to pick one of their alignments to preserve. This doesn't seem
correct in general; we can in fact can induce ld64 to produce a crashing
binary just by linking in an additional object file that only contains
cstrings and no code. See PR50563 for details.

Moreover, this scheme seems rather inefficient: since unaligned and
aligned strings are all put in the same section, which has a single
alignment value, it doesn't seem possible to tell whether a given string
doesn't have any alignment requirements. Preserving offset+alignments
for strings that don't need it is wasteful.

In practice, the crashes seen so far seem to stem from x86_64 SIMD
operations on cstrings. X86_64 requires SIMD accesses to be
16-byte-aligned. So for now, I'm thinking of just aligning all strings
to 16 bytes on x86_64. This is indeed wasteful, but implementation-wise
it's simpler than preserving per-string alignment+offsets. It also
avoids the aforementioned crash after deduplication of
differently-aligned strings. Finally, the overhead is not huge: using
16-byte alignment (vs no alignment) is only a 0.5% size overhead when
linking chromium_framework.

With these alignment requirements, it doesn't make sense to attempt tail
merging -- most strings will not be eligible since their overlaps aren't
likely to start at a 16-byte boundary. Tail-merging (with alignment) for
chromium_framework only improves size by 0.3%.

It's worth noting that LLD-ELF only does tail merging at `-O2`. By
default (at `-O1`), it just deduplicates w/o tail merging. @thakis has
also mentioned that they saw it regress compressed size in some cases
and therefore turned it off. `ld64` does not seem to do tail merging at
all.

**Performance Numbers**

CString deduplication reduces chromium_framework from 250MB to 242MB, or
about a 3.2% reduction.

Numbers for linking chromium_framework on my 3.2 GHz 16-Core Intel Xeon W:

      N           Min           Max        Median           Avg        Stddev
  x  20          3.91          4.03         3.935          3.95   0.034641016
  +  20          3.99          4.14         4.015        4.0365     0.0492336
  Difference at 95.0% confidence
          0.0865 +/- 0.027245
          2.18987% +/- 0.689746%
          (Student's t, pooled s = 0.0425673)

As expected, cstring merging incurs some non-trivial overhead.

When passing `--no-literal-merge`, it seems that performance is the
same, i.e. the refactoring in this diff didn't cost us.

      N           Min           Max        Median           Avg        Stddev
  x  20          3.91          4.03         3.935          3.95   0.034641016
  +  20          3.89          4.02         3.935        3.9435   0.043197831
  No difference proven at 95.0% confidence

Reviewed By: #lld-macho, gkm

Differential Revision: https://reviews.llvm.org/D102964


  Commit: 447dfbe005a7884766c7d073566eae710aa9ed5b
      https://github.com/llvm/llvm-project/commit/447dfbe005a7884766c7d073566eae710aa9ed5b
  Author: Jez Ng <jezng at fb.com>
  Date:   2021-06-07 (Mon, 07 Jun 2021)

  Changed paths:
    M lld/MachO/Config.h
    M lld/MachO/Driver.cpp
    M lld/MachO/Options.td
    A lld/test/MachO/force-load-swift-libs.ll

  Log Message:
  -----------
  [lld-macho] Implement -force_load_swift_libs

It causes libraries whose names start with "swift" to be force-loaded.
Note that unlike the more general `-force_load`, this flag only applies
to libraries specified via LC_LINKER_OPTIONS, and not those passed on
the command-line. This is what ld64 does.

Reviewed By: #lld-macho, thakis

Differential Revision: https://reviews.llvm.org/D103709


Compare: https://github.com/llvm/llvm-project/compare/310d2b4957c8...447dfbe005a7


More information about the All-commits mailing list