Index: lld/MachO/Arch/ARM64.cpp =================================================================== --- lld/MachO/Arch/ARM64.cpp +++ lld/MachO/Arch/ARM64.cpp @@ -36,6 +36,8 @@ uint64_t entryAddr) const override; const RelocAttrs &getRelocAttrs(uint8_t type) const override; void populateThunk(InputSection *thunk, Symbol *funcSym) override; + void applyOptimizationHints(uint8_t *, const ConcatInputSection *, + ArrayRef) const override; }; } // namespace @@ -150,6 +152,207 @@ stubHelperEntrySize = sizeof(stubHelperEntryCode); } +namespace { +struct Adrp { + uint32_t destRegister; +}; + +struct Add { + uint8_t destRegister; + uint8_t srcRegister; + uint32_t addend; +}; + +struct PerformedReloc { + const Reloc &rel; + uint64_t referentVA; +}; + +class OptimizationHintContext { +public: + OptimizationHintContext(uint8_t *buf, const ConcatInputSection *isec, + ArrayRef relocTargets) + : buf(buf), isec(isec), relocTargets(relocTargets), + relocIt(isec->relocs.rbegin()) {} + + void applyAdrpAdd(const OptimizationHint &); + void applyAdrpAdrp(const OptimizationHint &); + +private: + uint8_t *buf; + const ConcatInputSection *isec; + ArrayRef relocTargets; + std::vector::const_reverse_iterator relocIt; + + uint64_t getRelocTarget(const Reloc &); + + Optional findPrimaryReloc(uint64_t offset); + Optional findReloc(uint64_t offset); +}; +} // namespace + +static bool parseAdrp(uint32_t insn, Adrp &adrp) { + if ((insn & 0x9f000000) != 0x90000000) + return false; + adrp.destRegister = insn & 0x1f; + return true; +} + +static bool parseAdd(uint32_t insn, Add &add) { + if ((insn & 0xffc00000) != 0x91000000) + return false; + add.destRegister = insn & 0x1f; + add.srcRegister = (insn >> 5) & 0x1f; + add.addend = (insn >> 10) & 0xfff; + return true; +} + +static void writeAdr(void *loc, uint32_t dest, int32_t delta) { + uint32_t opcode = 0x10000000; + uint32_t immHi = (delta & 0x001ffffc) << 3; + uint32_t immLo = (delta & 0x00000003) << 29; + write32le(loc, opcode | immHi | immLo | dest); +} + +static void writeNop(void *loc) { write32le(loc, 0xd503201f); } + +uint64_t OptimizationHintContext::getRelocTarget(const Reloc &reloc) { + size_t relocIdx = &reloc - isec->relocs.data(); + return relocTargets[relocIdx]; +} + +// Optimization hints are sorted in a monotonically increasing order by their +// first address as are relocations (albeit in decreasing order), so if we keep +// a pointer around to the last found relocation, we don't have to do a full +// binary search every time. +Optional +OptimizationHintContext::findPrimaryReloc(uint64_t offset) { + const auto end = isec->relocs.rend(); + while (relocIt != end && relocIt->offset < offset) + ++relocIt; + if (relocIt == end || relocIt->offset != offset) + return None; + return PerformedReloc{*relocIt, getRelocTarget(*relocIt)}; +} + +// The second and third addresses of optimization hints have no such +// monotonicity as the first, so we search the entire range of relocations. +Optional OptimizationHintContext::findReloc(uint64_t offset) { + // Optimization hints often apply to successive relocations, so we check for + // that first before doing a full binary search. + auto end = isec->relocs.rend(); + if (relocIt < end - 1 && (relocIt + 1)->offset == offset) + return PerformedReloc{*(relocIt + 1), getRelocTarget(*(relocIt + 1))}; + + auto reloc = lower_bound(isec->relocs, offset, + [](const Reloc &reloc, uint64_t offset) { + return offset < reloc.offset; + }); + + if (reloc == isec->relocs.end() || reloc->offset != offset) + return None; + return PerformedReloc{*reloc, getRelocTarget(*reloc)}; +} + +// Transforms a pair of adrp+add instructions into an adr instruction if the +// target is within the +/- 1 MiB range allowed by the adr's 21 bit signed +// immediate offset. +// +// adrp xN, _foo@PAGE +// add xM, xN, _foo@PAGEOFF +// -> +// adr xM, _foo +// nop +void OptimizationHintContext::applyAdrpAdd(const OptimizationHint &hint) { + uint32_t ins1 = read32le(buf + hint.offset0); + uint32_t ins2 = read32le(buf + hint.offset0 + hint.delta[0]); + Adrp adrp; + if (!parseAdrp(ins1, adrp)) + return; + Add add; + if (!parseAdd(ins2, add)) + return; + if (adrp.destRegister != add.srcRegister) + return; + + Optional rel1 = findPrimaryReloc(hint.offset0); + Optional rel2 = findReloc(hint.offset0 + hint.delta[0]); + if (!rel1 || !rel2) + return; + if (rel1->referentVA != rel2->referentVA) + return; + int64_t delta = rel1->referentVA - rel1->rel.offset - isec->getVA(); + if (delta >= (1 << 20) || delta < -(1 << 20)) + return; + + writeAdr(buf + hint.offset0, add.destRegister, delta); + writeNop(buf + hint.offset0 + hint.delta[0]); +} + +// Transforms two adrp instructions into a single adrp if their referent +// addresses are located on the same 4096 byte page. +// +// adrp xN, _foo@PAGE +// adrp xN, _bar@PAGE +// -> +// adrp xN, _foo@PAGE +// nop +void OptimizationHintContext::applyAdrpAdrp(const OptimizationHint &hint) { + uint32_t ins1 = read32le(buf + hint.offset0); + uint32_t ins2 = read32le(buf + hint.offset0 + hint.delta[0]); + Adrp adrp1, adrp2; + if (!parseAdrp(ins1, adrp1) || !parseAdrp(ins2, adrp2)) + return; + if (adrp1.destRegister != adrp2.destRegister) + return; + + Optional rel1 = findPrimaryReloc(hint.offset0); + Optional rel2 = findReloc(hint.offset0 + hint.delta[0]); + if (!rel1 || !rel2) + return; + if ((rel1->referentVA & ~0xfffULL) != (rel2->referentVA & ~0xfffULL)) + return; + + writeNop(buf + hint.offset0 + hint.delta[0]); +} + +void ARM64::applyOptimizationHints(uint8_t *buf, const ConcatInputSection *isec, + ArrayRef relocTargets) const { + assert(isec); + assert(relocTargets.size() == isec->relocs.size()); + + // Note: Some of these optimizations might not be valid when shared regions + // are in use. Will need to revisit this if splitSegInfo is added. + + OptimizationHintContext ctx1(buf, isec, relocTargets); + for (const OptimizationHint &hint : isec->optimizationHints) { + switch (hint.type) { + case LOH_ARM64_ADRP_ADRP: + // This is done in another pass because the other optimization hints + // might cause its targets to be turned into NOPs. + break; + case LOH_ARM64_ADRP_LDR: + case LOH_ARM64_ADRP_ADD_LDR: + case LOH_ARM64_ADRP_LDR_GOT_LDR: + case LOH_ARM64_ADRP_ADD_STR: + case LOH_ARM64_ADRP_LDR_GOT_STR: + // TODO: Implement these + break; + case LOH_ARM64_ADRP_ADD: + ctx1.applyAdrpAdd(hint); + break; + case LOH_ARM64_ADRP_LDR_GOT: + // TODO: Implement this as well + break; + } + } + + OptimizationHintContext ctx2(buf, isec, relocTargets); + for (const OptimizationHint &hint : isec->optimizationHints) + if (hint.type == LOH_ARM64_ADRP_ADRP) + ctx2.applyAdrpAdrp(hint); +} + TargetInfo *macho::createARM64TargetInfo() { static ARM64 t; return &t; Index: lld/MachO/Config.h =================================================================== --- lld/MachO/Config.h +++ lld/MachO/Config.h @@ -130,6 +130,7 @@ bool dedupLiterals = true; bool omitDebugInfo = false; bool warnDylibInstallName = false; + bool ignoreOptimizationHints = false; // Temporary config flag that will be removed once we have fully implemented // support for __eh_frame. bool parseEhFrames = false; Index: lld/MachO/Driver.cpp =================================================================== --- lld/MachO/Driver.cpp +++ lld/MachO/Driver.cpp @@ -1301,6 +1301,7 @@ config->icfLevel != ICFLevel::none; config->warnDylibInstallName = args.hasFlag( OPT_warn_dylib_install_name, OPT_no_warn_dylib_install_name, false); + config->ignoreOptimizationHints = args.hasArg(OPT_ignore_optimization_hints); config->callGraphProfileSort = args.hasFlag( OPT_call_graph_profile_sort, OPT_no_call_graph_profile_sort, true); config->printSymbolOrder = args.getLastArgValue(OPT_print_symbol_order); Index: lld/MachO/InputFiles.h =================================================================== --- lld/MachO/InputFiles.h +++ lld/MachO/InputFiles.h @@ -173,6 +173,7 @@ std::vector debugSections; std::vector callGraph; llvm::DenseMap fdes; + std::vector optimizationHints; private: llvm::once_flag initDwarf; @@ -188,6 +189,7 @@ void parseRelocations(ArrayRef sectionHeaders, const SectionHeader &, Section &); void parseDebugInfo(); + void parseOptimizationHints(ArrayRef data); void splitEhFrames(ArrayRef dataArr, Section &ehFrameSection); void registerCompactUnwind(Section &compactUnwindSection); void registerEhFrames(Section &ehFrameSection); Index: lld/MachO/InputFiles.cpp =================================================================== --- lld/MachO/InputFiles.cpp +++ lld/MachO/InputFiles.cpp @@ -65,6 +65,7 @@ #include "llvm/LTO/LTO.h" #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/Endian.h" +#include "llvm/Support/LEB128.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" @@ -449,6 +450,154 @@ return *it; } +// Linker optimization hints mark a sequence of instructions used for +// synthesizing an address which that be transformed into a faster sequence. The +// transformations depend on conditions that are determined at link time, like +// the distance to the referenced symbol or its alignment. +// +// Each hint has a type and refers to 2 or 3 instructions. Each of those +// instructions must have a corresponding relocation. After addresses have been +// finalized and relocations have been performed, we check if the requirements +// hold, and perform the optimizations if they do. +// +// Similar linker relaxations exist for ELF as well, with the difference being +// that the explicit marking allows for the relaxation of non-consecutive +// relocations too. +// +// The specific types of hints are documented in Arch/ARM64.cpp +void ObjFile::parseOptimizationHints(ArrayRef data) { + auto expectedArgCount = [](uint8_t type) { + switch (type) { + case LOH_ARM64_ADRP_ADRP: + case LOH_ARM64_ADRP_LDR: + case LOH_ARM64_ADRP_ADD: + case LOH_ARM64_ADRP_LDR_GOT: + return 2; + case LOH_ARM64_ADRP_ADD_LDR: + case LOH_ARM64_ADRP_ADD_STR: + case LOH_ARM64_ADRP_LDR_GOT_LDR: + case LOH_ARM64_ADRP_LDR_GOT_STR: + return 3; + } + return -1; + }; + + // Each hint contains at least 4 ULEB128-encoded fields, so in the worst case, + // there are data.size() / 4 LOHs. It's a huge overestimation though, as + // offsets are unlikely to fall in the 0-127 byte range, so we pre-allocate + // half as much. + optimizationHints.reserve(data.size() / 8); + + for (const uint8_t *p = data.begin(); p < data.end();) { + const ptrdiff_t inputOffset = p - data.begin(); + unsigned int n = 0; + uint8_t type = decodeULEB128(p, &n, data.end()); + p += n; + + // An entry of type 0 terminates the list. + if (type == 0) + break; + + int expectedCount = expectedArgCount(type); + if (LLVM_UNLIKELY(expectedCount == -1)) { + error("Linker optimization hint at offset " + Twine(inputOffset) + + " has unknown type " + Twine(type)); + return; + } + + uint8_t argCount = decodeULEB128(p, &n, data.end()); + p += n; + + if (LLVM_UNLIKELY(argCount != expectedCount)) { + error("Linker optimization hint at offset " + Twine(inputOffset) + + " has " + Twine(argCount) + " arguments instead of the expected " + + Twine(expectedCount)); + return; + } + + uint64_t offset0 = decodeULEB128(p, &n, data.end()); + p += n; + + int16_t delta[2]; + for (int i = 0; i < argCount - 1; ++i) { + uint64_t address = decodeULEB128(p, &n, data.end()); + p += n; + int64_t d = address - offset0; + if (LLVM_UNLIKELY(d > std::numeric_limits::max() || + d < std::numeric_limits::min())) { + error("Linker optimization hint at offset " + Twine(inputOffset) + + " has addresses too far apart"); + return; + } + delta[i] = d; + } + + optimizationHints.push_back({offset0, {delta[0], delta[1]}, type}); + } + + // We sort the per-object vector of optimization hints so each section only + // needs to hold an ArrayRef to a contiguous range of hints. + llvm::sort(optimizationHints, + [](const OptimizationHint &a, const OptimizationHint &b) { + return a.offset0 < b.offset0; + }); + + auto section = sections.begin(); + auto subsection = (*section)->subsections.begin(); + uint64_t subsectionBase = 0; + uint64_t subsectionEnd = 0; + + auto updateAddr = [&]() { + subsectionBase = (*section)->addr + subsection->offset; + subsectionEnd = subsectionBase + subsection->isec->getSize(); + }; + + auto advanceSubsection = [&]() { + if (section == sections.end()) + return; + ++subsection; + if (subsection == (*section)->subsections.end()) { + ++section; + if (section == sections.end()) + return; + subsection = (*section)->subsections.begin(); + } + }; + + updateAddr(); + auto hintStart = optimizationHints.begin(); + for (auto hintEnd = hintStart, end = optimizationHints.end(); hintEnd != end; + ++hintEnd) { + if (hintEnd->offset0 >= subsectionEnd) { + subsection->isec->optimizationHints = + ArrayRef(&*hintStart, hintEnd - hintStart); + + hintStart = hintEnd; + while (hintStart->offset0 >= subsectionEnd) { + advanceSubsection(); + if (section == sections.end()) + break; + updateAddr(); + } + } + + hintEnd->offset0 -= subsectionBase; + for (int i = 0, count = expectedArgCount(hintEnd->type); i < count - 1; + ++i) { + if (LLVM_UNLIKELY( + hintEnd->delta[i] < -static_cast(hintEnd->offset0) || + hintEnd->delta[i] >= + static_cast(subsectionEnd - hintEnd->offset0))) { + error("Linker optimization hint spans multiple sections"); + return; + } + } + } + if (section != sections.end()) + subsection->isec->optimizationHints = ArrayRef( + &*hintStart, optimizationHints.end() - hintStart); +} + template static bool validateRelocationInfo(InputFile *file, const SectionHeader &sec, relocation_info rel) { @@ -949,6 +1098,11 @@ if (!sections[i]->subsections.empty()) parseRelocations(sectionHeaders, sectionHeaders[i], *sections[i]); + if (!config->ignoreOptimizationHints) + if (auto *cmd = findCommand( + hdr, LC_LINKER_OPTIMIZATION_HINT)) + parseOptimizationHints({buf + cmd->dataoff, cmd->datasize}); + parseDebugInfo(); Section *ehFrameSection = nullptr; Index: lld/MachO/InputSection.h =================================================================== --- lld/MachO/InputSection.h +++ lld/MachO/InputSection.h @@ -83,6 +83,7 @@ OutputSection *parent = nullptr; ArrayRef data; std::vector relocs; + ArrayRef optimizationHints; // The symbols that belong to this InputSection, sorted by value. With // .subsections_via_symbols, there is typically only one element here. llvm::TinyPtrVector symbols; Index: lld/MachO/InputSection.cpp =================================================================== --- lld/MachO/InputSection.cpp +++ lld/MachO/InputSection.cpp @@ -29,8 +29,8 @@ // Verify ConcatInputSection's size on 64-bit builds. The size of std::vector // can differ based on STL debug levels (e.g. iterator debugging on MSVC's STL), // so account for that. -static_assert(sizeof(void *) != 8 || - sizeof(ConcatInputSection) == sizeof(std::vector) + 88, +static_assert(sizeof(void *) != 8 || sizeof(ConcatInputSection) == + sizeof(std::vector) + 104, "Try to minimize ConcatInputSection's size, we create many " "instances of it"); @@ -177,6 +177,10 @@ memcpy(buf, data.data(), data.size()); + std::vector relocTargets; + if (!optimizationHints.empty()) + relocTargets.reserve(relocs.size()); + for (size_t i = 0; i < relocs.size(); i++) { const Reloc &r = relocs[i]; uint8_t *loc = buf + r.offset; @@ -212,7 +216,13 @@ referentVA = referentIsec->getVA(r.addend); } target->relocateOne(loc, r, referentVA, getVA() + r.offset); + + if (!optimizationHints.empty()) + relocTargets.push_back(referentVA); } + + if (!optimizationHints.empty()) + target->applyOptimizationHints(buf, this, relocTargets); } ConcatInputSection *macho::makeSyntheticInputSection(StringRef segName, Index: lld/MachO/Options.td =================================================================== --- lld/MachO/Options.td +++ lld/MachO/Options.td @@ -1257,8 +1257,7 @@ Flags<[HelpHidden]>, Group; def ignore_optimization_hints : Flag<["-"], "ignore_optimization_hints">, - HelpText<"This option is undocumented in ld64">, - Flags<[HelpHidden]>, + HelpText<"Ignore Linker Optimization Hints">, Group; def init_offsets : Flag<["-"], "init_offsets">, HelpText<"This option is undocumented in ld64">, Index: lld/MachO/Relocations.h =================================================================== --- lld/MachO/Relocations.h +++ lld/MachO/Relocations.h @@ -70,6 +70,14 @@ addend(addend), referent(referent) {} }; +struct OptimizationHint { + // Offset of the first address within the containing InputSection. + uint64_t offset0; + // Offset of the other addresses relative to the first one. + int16_t delta[2]; + uint8_t type; +}; + bool validateSymbolRelocation(const Symbol *, const InputSection *, const Reloc &); Index: lld/MachO/Target.h =================================================================== --- lld/MachO/Target.h +++ lld/MachO/Target.h @@ -27,7 +27,8 @@ class Symbol; class Defined; class DylibSymbol; -class InputSection; +class ConcatInputSection; +struct OptimizationHint; class TargetInfo { public: @@ -78,6 +79,9 @@ bool usesThunks() const { return thunkSize > 0; } + virtual void applyOptimizationHints(uint8_t *buf, const ConcatInputSection *, + llvm::ArrayRef) const {}; + uint32_t magic; llvm::MachO::CPUType cpuType; uint32_t cpuSubtype; Index: lld/test/MachO/invalid/invalid-loh.s =================================================================== --- /dev/null +++ lld/test/MachO/invalid/invalid-loh.s @@ -0,0 +1,39 @@ +# REQUIRES: aarch64 + +# RUN: rm -rf %t; split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=arm64-apple-darwin %t/section.s -o %t/section.o +# RUN: llvm-mc -filetype=obj -triple=arm64-apple-darwin %t/far.s -o %t/far.o +# RUN: not %lld -arch arm64 %t/section.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=SECTION +# RUN: not %lld -arch arm64 %t/far.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=FAR + +# SECTION: error: Linker optimization hint spans multiple sections +# FAR: error: Linker optimization hint at offset 0 has addresses too far apart + +#--- section.s +.globl _main +_main: +L1: + adrp x0, _target@PAGE + +_foo: +L2: + add x0, x0, _target@PAGEOFF + +_target: + +.loh AdrpAdd L1, L2 +.subsections_via_symbols + +#--- far.s +.globl _main +_main: +L1: + adrp x0, _target@PAGE + .zero 0x8000 +L2: + add x0, x0, _target@PAGEOFF + +_target: + +.loh AdrpAdd L1, L2 +.subsections_via_symbols Index: lld/test/MachO/loh-adrp-add.s =================================================================== --- /dev/null +++ lld/test/MachO/loh-adrp-add.s @@ -0,0 +1,98 @@ +# REQUIRES: aarch64 + +# RUN: llvm-mc -filetype=obj -triple=arm64-apple-darwin %s -o %t.o +# RUN: %lld -arch arm64 %t.o -o %t +# RUN: llvm-objdump -d --macho %t | FileCheck %s + +# CHECK-LABEL: _main: +## Out of range, before +# CHECK-NEXT: adrp x0 +# CHECK-NEXT: add x0, x0 +## In range, before +# CHECK-NEXT: adr x1 +# CHECK-NEXT: nop +## Registers don't match (invalid input) +# CHECK-NEXT: adrp x2 +# CHECK-NEXT: add x0 +## Targets don't match (invalid input) +# CHECK-NEXT: adrp x3 +# CHECK-NEXT: add x3 +## Not an adrp instruction (invalid input) +# CHECK-NEXT: nop +# CHECK-NEXT: add x4 +## In range, after +# CHECK-NEXT: adr x5 +# CHECK-NEXT: nop +## In range, add's destination register is not the same as its source +# CHECK-NEXT: adr x7 +# CHECK-NEXT: nop +## Valid, non-adjacent instructions - start +# CHECK-NEXT: adr x8 +## Out of range, after +# CHECK-NEXT: adrp x9 +# CHECK-NEXT: add x9, x9 +## Valid, non-adjacent instructions - end +# CHECK-NEXT: nop + +.text +.align 2 +_before_far: + .space 1048576 + +_before_near: + nop + +.globl _main +_main: +L1: + adrp x0, _before_far@PAGE +L2: + add x0, x0, _before_far@PAGEOFF +L3: + adrp x1, _before_near@PAGE +L4: + add x1, x1, _before_near@PAGEOFF +L5: + adrp x2, _before_near@PAGE +L6: + add x0, x0, _before_near@PAGEOFF +L7: + adrp x3, _before_near@PAGE +L8: + add x3, x3, _after_near@PAGEOFF +L9: + nop +L10: + add x4, x4, _after_near@PAGEOFF +L11: + adrp x5, _after_near@PAGE +L12: + add x5, x5, _after_near@PAGEOFF +L13: + adrp x6, _after_near@PAGE +L14: + add x7, x6, _after_near@PAGEOFF +L15: + adrp x8, _after_near@PAGE +L16: + adrp x9, _after_far@PAGE +L17: + add x9, x9, _after_far@PAGEOFF +L18: + add x8, x8, _after_near@PAGEOFF + +_after_near: + .space 1048576 + +_after_far: + nop + +.loh AdrpAdd L1, L2 +.loh AdrpAdd L3, L4 +.loh AdrpAdd L5, L6 +.loh AdrpAdd L7, L8 +.loh AdrpAdd L9, L10 +.loh AdrpAdd L11, L12 +.loh AdrpAdd L13, L14 +.loh AdrpAdd L15, L18 +.loh AdrpAdd L16, L17 Index: lld/test/MachO/loh-adrp-adrp.s =================================================================== --- /dev/null +++ lld/test/MachO/loh-adrp-adrp.s @@ -0,0 +1,56 @@ +# REQUIRES: aarch64 + +# RUN: llvm-mc -filetype=obj -triple=arm64-apple-darwin %s -o %t.o +# RUN: %lld -arch arm64 %t.o -o %t +# RUN: llvm-objdump -d --macho %t | FileCheck %s + +# CHECK-LABEL: _main: +## Valid +# CHECK-NEXT: adrp x0 +# CHECK-NEXT: nop +## Mismatched registers +# CHECK-NEXT: adrp x1 +# CHECK-NEXT: adrp x2 +## Not on the same page +# CHECK-NEXT: adrp x3 +# CHECK-NEXT: adrp x3 +## Not an adrp instruction (invalid) +# CHECK-NEXT: nop +# CHECK-NEXT: adrp x4 + +.text +.align 2 + +.globl _main +_main: +L1: + adrp x0, _foo@PAGE +L2: + adrp x0, _bar@PAGE +L3: + adrp x1, _foo@PAGE +L4: + adrp x2, _bar@PAGE +L5: + adrp x3, _foo@PAGE +L6: + adrp x3, _baz@PAGE +L7: + nop +L8: + adrp x4, _baz@PAGE + +.data +.align 12 +_foo: + .byte 0 +_bar: + .byte 0 +.space 4094 +_baz: + .byte 0 + +.loh AdrpAdrp L1, L2 +.loh AdrpAdrp L3, L4 +.loh AdrpAdrp L5, L6 +.loh AdrpAdrp L7, L8 Index: llvm/include/llvm/BinaryFormat/MachO.h =================================================================== --- llvm/include/llvm/BinaryFormat/MachO.h +++ llvm/include/llvm/BinaryFormat/MachO.h @@ -2237,6 +2237,17 @@ kSecCodeSignatureHashSHA512 = 5, /* SHA-512 */ }; +enum LinkerOptimizationHintKind { + LOH_ARM64_ADRP_ADRP = 1, + LOH_ARM64_ADRP_LDR = 2, + LOH_ARM64_ADRP_ADD_LDR = 3, + LOH_ARM64_ADRP_LDR_GOT_LDR = 4, + LOH_ARM64_ADRP_ADD_STR = 5, + LOH_ARM64_ADRP_LDR_GOT_STR = 6, + LOH_ARM64_ADRP_ADD = 7, + LOH_ARM64_ADRP_LDR_GOT = 8, +}; + } // end namespace MachO } // end namespace llvm