From 8d29f0fdb9c64087687ac80074876d47b16efeab Mon Sep 17 00:00:00 2001 From: Daniel Bertalan Date: Wed, 29 Jun 2022 11:33:41 +0200 Subject: [PATCH] [lld-macho] Emit REBASE_OPCODE_ADD_ADDR_IMM_SCALED if possible An ADD_ADDR rebase opcode's argument can be encoded as an immediate if the offset is less than 15 * word size. This change reduces the size of chromium_framework by 100+ KiB. Differential Revision: https://reviews.llvm.org/D128798 --- lld/MachO/SyntheticSections.cpp | 14 +++++++++++-- lld/test/MachO/rebase-opcodes.s | 45 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 lld/test/MachO/rebase-opcodes.s diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp index dabe158..0a57de3 100644 --- a/lld/MachO/SyntheticSections.cpp +++ b/lld/MachO/SyntheticSections.cpp @@ -202,8 +202,18 @@ static void encodeRebase(const OutputSection *osec, uint64_t outSecOff, lastRebase.offset = offset; } else { assert(lastRebase.offset != offset); - os << static_cast(REBASE_OPCODE_ADD_ADDR_ULEB); - encodeULEB128(offset - lastRebase.offset, os); + uint64_t delta = offset - lastRebase.offset; + // For unknown reasons, ld64 checks if the scaled offset is strictly less + // than REBASE_IMMEDIATE_MASK instead of allowing equality. We match this + // behavior as a precaution. + if ((delta % target->wordSize == 0) && + (delta / target->wordSize < REBASE_IMMEDIATE_MASK)) { + os << static_cast(REBASE_OPCODE_ADD_ADDR_IMM_SCALED | + (delta / target->wordSize)); + } else { + os << static_cast(REBASE_OPCODE_ADD_ADDR_ULEB); + encodeULEB128(delta, os); + } lastRebase.offset = offset; } } diff --git a/lld/test/MachO/rebase-opcodes.s b/lld/test/MachO/rebase-opcodes.s new file mode 100644 index 0000000..21b68f7 --- /dev/null +++ b/lld/test/MachO/rebase-opcodes.s @@ -0,0 +1,45 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o +# RUN: %lld -dylib %t.o -o %t.dylib +# RUN: obj2yaml %t.dylib | FileCheck %s + +## Test that: +## 1/ Consecutive rebases are encoded as REBASE_OPCODE_DO_REBASE_IMM_TIMES. +## 2/ Gaps smaller than 15 words are encoded as REBASE_OPCODE_ADD_ADDR_IMM_SCALED. +## 3/ Gaps larger than that become REBASE_OPCODE_ADD_ADDR_ULEB. +## FIXME: The last rebase could be transformed into a REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB. + +# CHECK: RebaseOpcodes: +# CHECK-NEXT: Opcode: REBASE_OPCODE_SET_TYPE_IMM +# CHECK-NEXT: Imm: 1 +# CHECK-NEXT: Opcode: REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB +# CHECK-NEXT: Imm: 1 +# CHECK-NEXT: ExtraData: [ 0x0 ] +# CHECK-NEXT: Opcode: REBASE_OPCODE_DO_REBASE_IMM_TIMES +# CHECK-NEXT: Imm: 1 +# CHECK-NEXT: Opcode: REBASE_OPCODE_ADD_ADDR_IMM_SCALED +# CHECK-NEXT: Imm: 14 +# CHECK-NEXT: Opcode: REBASE_OPCODE_DO_REBASE_IMM_TIMES +# CHECK-NEXT: Imm: 3 +# CHECK-NEXT: Opcode: REBASE_OPCODE_ADD_ADDR_ULEB +# CHECK-NEXT: Imm: 0 +# CHECK-NEXT: ExtraData: [ 0x78 ] +# CHECK-NEXT: Opcode: REBASE_OPCODE_DO_REBASE_IMM_TIMES +# CHECK-NEXT: Imm: 1 +# CHECK-NEXT: Opcode: REBASE_OPCODE_DONE +# CHECK-NEXT: Imm: 0 + + +.text +.globl _foo +_foo: + +.data +.quad _foo +.space 112 +.quad _foo +.quad _foo +.quad _foo +.space 120 +.quad _foo -- 2.7.4