+ case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12:
+ accumulator = addressOf(state, fit, &toTarget);
+ // fall into kindStoreARM64TLVPLeaPageOff12 case
+ case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPageOff12:
+ {
+ // TLV thunk in same linkage unit, so LEA it directly, changing LDR instruction to a ADD
+ instruction = get32LE(fixUpLocation);
+ if ( (instruction & 0xFFC00000) != 0xF9400000 )
+ throwf("TLV load reloc does not point to a LDR instruction in %s", atom->name());
+ uint32_t offset = accumulator & 0x00000FFF;
+ uint32_t imm12 = offset << 10;
+ newInstruction = 0x91000000 | imm12 | (instruction & 0x000003FF);
+ set32LE(fixUpLocation, newInstruction);
+ }
+ break;
+ case ld::Fixup::kindStoreARM64PointerToGOT:
+ set64LE(fixUpLocation, accumulator);
+ break;
+ case ld::Fixup::kindStoreARM64PCRelToGOT:
+ if ( fit->contentAddendOnly )
+ delta = accumulator;
+ else
+ delta = accumulator - (atom->finalAddress() + fit->offsetInAtom);
+ set32LE(fixUpLocation, delta);
+ break;
+#endif
+ }
+ }
+
+#if SUPPORT_ARCH_arm64
+ // after all fixups are done on atom, if there are potential optimizations, do those
+ if ( (usedByHints.size() != 0) && (_options.outputKind() != Options::kObjectFile) && !_options.ignoreOptimizationHints() ) {
+ // fill in second part of usedByHints map, so we can see the target of fixups that might be optimized
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
+ switch ( fit->kind ) {
+ case ld::Fixup::kindLinkerOptimizationHint:
+ case ld::Fixup::kindNoneFollowOn:
+ case ld::Fixup::kindNoneGroupSubordinate:
+ case ld::Fixup::kindNoneGroupSubordinateFDE:
+ case ld::Fixup::kindNoneGroupSubordinateLSDA:
+ case ld::Fixup::kindNoneGroupSubordinatePersonality:
+ break;
+ default:
+ if ( fit->firstInCluster() ) {
+ std::map<uint32_t, const Fixup*>::iterator pos = usedByHints.find(fit->offsetInAtom);
+ if ( pos != usedByHints.end() ) {
+ assert(pos->second == NULL && "two fixups in same hint location");
+ pos->second = fit;
+ //fprintf(stderr, "setting %s usedByHints[0x%04X], kind = %d\n", atom->name(), fit->offsetInAtom, fit->kind);
+ }
+ }
+ }
+ }
+
+ // apply hints pass 1
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
+ if ( fit->kind != ld::Fixup::kindLinkerOptimizationHint )
+ continue;
+ InstructionInfo infoA;
+ InstructionInfo infoB;
+ InstructionInfo infoC;
+ InstructionInfo infoD;
+ LoadStoreInfo ldrInfoB, ldrInfoC;
+ AddInfo addInfoB;
+ AdrpInfo adrpInfoA;
+ bool usableSegment;
+ bool targetFourByteAligned;
+ bool literalableSize, isADRP, isADD, isLDR, isSTR;
+ //uint8_t loadSize, destReg;
+ //uint32_t scaledOffset;
+ //uint32_t imm12;
+ ld::Fixup::LOH_arm64 alt;
+ alt.addend = fit->u.addend;
+ setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta1 << 2), &infoA);
+ if ( alt.info.count > 0 )
+ setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta2 << 2), &infoB);
+ if ( alt.info.count > 1 )
+ setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta3 << 2), &infoC);
+ if ( alt.info.count > 2 )
+ setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta4 << 2), &infoD);
+
+ switch ( alt.info.kind ) {
+ case LOH_ARM64_ADRP_ADRP:
+ // processed in pass 2 beacuse some ADRP may have been removed
+ break;
+ case LOH_ARM64_ADRP_LDR:
+ LOH_ASSERT(alt.info.count == 1);
+ LOH_ASSERT(isPageKind(infoA.fixup));
+ LOH_ASSERT(isPageOffsetKind(infoB.fixup));
+ LOH_ASSERT(infoA.target == infoB.target);
+ LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ isADRP = parseADRP(infoA.instruction, adrpInfoA);
+ LOH_ASSERT(isADRP);
+ isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB);
+ LOH_ASSERT(isLDR);
+ LOH_ASSERT(ldrInfoB.baseReg == adrpInfoA.destReg);
+ LOH_ASSERT(ldrInfoB.offset == (infoA.targetAddress & 0x00000FFF));
+ literalableSize = ( (ldrInfoB.size != 1) && (ldrInfoB.size != 2) );
+ targetFourByteAligned = ( (infoA.targetAddress & 0x3) == 0 );
+ if ( literalableSize && usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) {
+ set32LE(infoA.instructionContent, makeNOP());
+ set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress));
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr at 0x%08llX transformed to LDR literal\n", infoB.instructionAddress);
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr at 0x%08llX not transformed, isLDR=%d, literalableSize=%d, inRange=%d, usableSegment=%d, scaledOffset=%d\n",
+ infoB.instructionAddress, isLDR, literalableSize, withinOneMeg(infoB.instructionAddress, infoA.targetAddress), usableSegment, ldrInfoB.offset);
+ }
+ break;
+ case LOH_ARM64_ADRP_ADD_LDR:
+ LOH_ASSERT(alt.info.count == 2);
+ LOH_ASSERT(isPageKind(infoA.fixup));
+ LOH_ASSERT(isPageOffsetKind(infoB.fixup));
+ LOH_ASSERT(infoC.fixup == NULL);
+ LOH_ASSERT(infoA.target == infoB.target);
+ LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ isADRP = parseADRP(infoA.instruction, adrpInfoA);
+ LOH_ASSERT(isADRP);
+ isADD = parseADD(infoB.instruction, addInfoB);
+ LOH_ASSERT(isADD);
+ LOH_ASSERT(adrpInfoA.destReg == addInfoB.srcReg);
+ isLDR = parseLoadOrStore(infoC.instruction, ldrInfoC);
+ LOH_ASSERT(isLDR);
+ LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg);
+ targetFourByteAligned = ( ((infoB.targetAddress+ldrInfoC.offset) & 0x3) == 0 );
+ literalableSize = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) );
+ if ( literalableSize && usableSegment && targetFourByteAligned && withinOneMeg(infoC.instructionAddress, infoA.targetAddress+ldrInfoC.offset) ) {
+ // can do T1 transformation to LDR literal
+ set32LE(infoA.instructionContent, makeNOP());
+ set32LE(infoB.instructionContent, makeNOP());
+ set32LE(infoC.instructionContent, makeLDR_literal(ldrInfoC, infoA.targetAddress+ldrInfoC.offset, infoC.instructionAddress));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-add-ldr at 0x%08llX T1 transformed to LDR literal\n", infoC.instructionAddress);
+ }
+ }
+ else if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress+ldrInfoC.offset) ) {
+ // can to T4 transformation and turn ADRP/ADD into ADR
+ set32LE(infoA.instructionContent, makeADR(ldrInfoC.baseReg, infoA.targetAddress+ldrInfoC.offset, infoA.instructionAddress));
+ set32LE(infoB.instructionContent, makeNOP());
+ ldrInfoC.offset = 0; // offset is now in ADR instead of ADD or LDR
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ set32LE(infoC.instructionContent, infoC.instruction & 0xFFC003FF);
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add-ldr at 0x%08llX T4 transformed to ADR/LDR\n", infoB.instructionAddress);
+ }
+ else if ( ((infoB.targetAddress % ldrInfoC.size) == 0) && (ldrInfoC.offset == 0) ) {
+ // can do T2 transformation by merging ADD into LD
+ // Leave ADRP as-is
+ set32LE(infoB.instructionContent, makeNOP());
+ ldrInfoC.offset += addInfoB.addend;
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add-ldr at 0x%08llX T2 transformed to ADRP/LDR \n", infoC.instructionAddress);
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add-ldr at 0x%08llX could not be transformed, loadSize=%d, literalableSize=%d, inRange=%d, usableSegment=%d, targetFourByteAligned=%d, imm12=%d\n",
+ infoC.instructionAddress, ldrInfoC.size, literalableSize, withinOneMeg(infoC.instructionAddress, infoA.targetAddress+ldrInfoC.offset), usableSegment, targetFourByteAligned, ldrInfoC.offset);
+ }
+ break;
+ case LOH_ARM64_ADRP_ADD:
+ LOH_ASSERT(alt.info.count == 1);
+ LOH_ASSERT(isPageKind(infoA.fixup));
+ LOH_ASSERT(isPageOffsetKind(infoB.fixup));
+ LOH_ASSERT(infoA.target == infoB.target);
+ LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
+ isADRP = parseADRP(infoA.instruction, adrpInfoA);
+ LOH_ASSERT(isADRP);
+ isADD = parseADD(infoB.instruction, addInfoB);
+ LOH_ASSERT(isADD);
+ LOH_ASSERT(adrpInfoA.destReg == addInfoB.srcReg);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ if ( usableSegment && withinOneMeg(infoA.targetAddress, infoA.instructionAddress) ) {
+ // can do T4 transformation and use ADR
+ set32LE(infoA.instructionContent, makeADR(addInfoB.destReg, infoA.targetAddress, infoA.instructionAddress));
+ set32LE(infoB.instructionContent, makeNOP());
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add at 0x%08llX transformed to ADR\n", infoB.instructionAddress);
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add at 0x%08llX not transformed, isAdd=%d, inRange=%d, usableSegment=%d\n",
+ infoB.instructionAddress, isADD, withinOneMeg(infoA.targetAddress, infoA.instructionAddress), usableSegment);
+ }
+ break;
+ case LOH_ARM64_ADRP_LDR_GOT_LDR:
+ LOH_ASSERT(alt.info.count == 2);
+ LOH_ASSERT(isPageKind(infoA.fixup, true));
+ LOH_ASSERT(isPageOffsetKind(infoB.fixup, true));
+ LOH_ASSERT(infoC.fixup == NULL);
+ LOH_ASSERT(infoA.target == infoB.target);
+ LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
+ isADRP = parseADRP(infoA.instruction, adrpInfoA);
+ LOH_ASSERT(isADRP);
+ isLDR = parseLoadOrStore(infoC.instruction, ldrInfoC);
+ LOH_ASSERT(isLDR);
+ LOH_ASSERT(ldrInfoC.offset == 0);
+ isADD = parseADD(infoB.instruction, addInfoB);
+ isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB);
+ if ( isLDR ) {
+ // target of GOT is external
+ LOH_ASSERT(ldrInfoB.size == 8);
+ LOH_ASSERT(!ldrInfoB.isFloat);
+ LOH_ASSERT(ldrInfoC.baseReg == ldrInfoB.reg);
+ //fprintf(stderr, "infoA.target=%p, %s, infoA.targetAddress=0x%08llX\n", infoA.target, infoA.target->name(), infoA.targetAddress);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 );
+ if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) {
+ // can do T5 transform
+ set32LE(infoA.instructionContent, makeNOP());
+ set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T5 transformed to LDR literal of GOT plus LDR\n", infoC.instructionAddress);
+ }
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX no optimization done\n", infoC.instructionAddress);
+ }
+ }
+ else if ( isADD ) {
+ // target of GOT is in same linkage unit and B instruction was changed to ADD to compute LEA of target
+ LOH_ASSERT(addInfoB.srcReg == adrpInfoA.destReg);
+ LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 );
+ literalableSize = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) );
+ if ( usableSegment && literalableSize && targetFourByteAligned && withinOneMeg(infoC.instructionAddress, infoA.targetAddress) ) {
+ // can do T1 transform
+ set32LE(infoA.instructionContent, makeNOP());
+ set32LE(infoB.instructionContent, makeNOP());
+ set32LE(infoC.instructionContent, makeLDR_literal(ldrInfoC, infoA.targetAddress, infoC.instructionAddress));
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T1 transformed to LDR literal\n", infoC.instructionAddress);
+ }
+ else if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress) ) {
+ // can do T4 transform
+ set32LE(infoA.instructionContent, makeADR(ldrInfoC.baseReg, infoA.targetAddress, infoA.instructionAddress));
+ set32LE(infoB.instructionContent, makeNOP());
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T4 transformed to ADR/LDR\n", infoC.instructionAddress);
+ }
+ }
+ else if ( (infoA.targetAddress % ldrInfoC.size) == 0 ) {
+ // can do T2 transform
+ set32LE(infoB.instructionContent, makeNOP());
+ ldrInfoC.baseReg = adrpInfoA.destReg;
+ ldrInfoC.offset = addInfoB.addend;
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T4 transformed to ADRP/NOP/LDR\n", infoC.instructionAddress);
+ }
+ }
+ else {
+ // T3 transform already done by ld::passes:got:doPass()
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T3 transformed to ADRP/ADD/LDR\n", infoC.instructionAddress);
+ }
+ }
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX not ADD or LDR\n", infoC.instructionAddress);
+ }
+ break;
+ case LOH_ARM64_ADRP_ADD_STR:
+ LOH_ASSERT(alt.info.count == 2);
+ LOH_ASSERT(isPageKind(infoA.fixup));
+ LOH_ASSERT(isPageOffsetKind(infoB.fixup));
+ LOH_ASSERT(infoC.fixup == NULL);
+ LOH_ASSERT(infoA.target == infoB.target);
+ LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ isADRP = parseADRP(infoA.instruction, adrpInfoA);
+ LOH_ASSERT(isADRP);
+ isADD = parseADD(infoB.instruction, addInfoB);
+ LOH_ASSERT(isADD);
+ LOH_ASSERT(adrpInfoA.destReg == addInfoB.srcReg);
+ isSTR = (parseLoadOrStore(infoC.instruction, ldrInfoC) && ldrInfoC.isStore);
+ LOH_ASSERT(isSTR);
+ LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg);
+ if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress+ldrInfoC.offset) ) {
+ // can to T4 transformation and turn ADRP/ADD into ADR
+ set32LE(infoA.instructionContent, makeADR(ldrInfoC.baseReg, infoA.targetAddress+ldrInfoC.offset, infoA.instructionAddress));
+ set32LE(infoB.instructionContent, makeNOP());
+ ldrInfoC.offset = 0; // offset is now in ADR instead of ADD or LDR
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ set32LE(infoC.instructionContent, infoC.instruction & 0xFFC003FF);
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add-str at 0x%08llX T4 transformed to ADR/STR\n", infoB.instructionAddress);
+ }
+ else if ( ((infoB.targetAddress % ldrInfoC.size) == 0) && (ldrInfoC.offset == 0) ) {
+ // can do T2 transformation by merging ADD into STR
+ // Leave ADRP as-is
+ set32LE(infoB.instructionContent, makeNOP());
+ ldrInfoC.offset += addInfoB.addend;
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add-str at 0x%08llX T2 transformed to ADRP/STR \n", infoC.instructionAddress);
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-add-str at 0x%08llX could not be transformed, loadSize=%d, inRange=%d, usableSegment=%d, imm12=%d\n",
+ infoC.instructionAddress, ldrInfoC.size, withinOneMeg(infoC.instructionAddress, infoA.targetAddress+ldrInfoC.offset), usableSegment, ldrInfoC.offset);
+ }
+ break;
+ case LOH_ARM64_ADRP_LDR_GOT_STR:
+ LOH_ASSERT(alt.info.count == 2);
+ LOH_ASSERT(isPageKind(infoA.fixup, true));
+ LOH_ASSERT(isPageOffsetKind(infoB.fixup, true));
+ LOH_ASSERT(infoC.fixup == NULL);
+ LOH_ASSERT(infoA.target == infoB.target);
+ LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
+ isADRP = parseADRP(infoA.instruction, adrpInfoA);
+ LOH_ASSERT(isADRP);
+ isSTR = (parseLoadOrStore(infoC.instruction, ldrInfoC) && ldrInfoC.isStore);
+ LOH_ASSERT(isSTR);
+ LOH_ASSERT(ldrInfoC.offset == 0);
+ isADD = parseADD(infoB.instruction, addInfoB);
+ isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB);
+ if ( isLDR ) {
+ // target of GOT is external
+ LOH_ASSERT(ldrInfoB.size == 8);
+ LOH_ASSERT(!ldrInfoB.isFloat);
+ LOH_ASSERT(ldrInfoC.baseReg == ldrInfoB.reg);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 );
+ if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) {
+ // can do T5 transform
+ set32LE(infoA.instructionContent, makeNOP());
+ set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T5 transformed to LDR literal of GOT plus STR\n", infoC.instructionAddress);
+ }
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr-got-str at 0x%08llX no optimization done\n", infoC.instructionAddress);
+ }
+ }
+ else if ( isADD ) {
+ // target of GOT is in same linkage unit and B instruction was changed to ADD to compute LEA of target
+ LOH_ASSERT(addInfoB.srcReg == adrpInfoA.destReg);
+ LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 );
+ literalableSize = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) );
+ if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress) ) {
+ // can do T4 transform
+ set32LE(infoA.instructionContent, makeADR(ldrInfoC.baseReg, infoA.targetAddress, infoA.instructionAddress));
+ set32LE(infoB.instructionContent, makeNOP());
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T4 transformed to ADR/STR\n", infoC.instructionAddress);
+ }
+ }
+ else if ( ((infoA.targetAddress % ldrInfoC.size) == 0) && (ldrInfoC.offset == 0) ) {
+ // can do T2 transform
+ set32LE(infoB.instructionContent, makeNOP());
+ ldrInfoC.baseReg = adrpInfoA.destReg;
+ ldrInfoC.offset = addInfoB.addend;
+ set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T4 transformed to ADRP/NOP/STR\n", infoC.instructionAddress);
+ }
+ }
+ else {
+ // T3 transform already done by ld::passes:got:doPass()
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T3 transformed to ADRP/ADD/STR\n", infoC.instructionAddress);
+ }
+ }
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr-got-str at 0x%08llX not ADD or LDR\n", infoC.instructionAddress);
+ }
+ break;
+ case LOH_ARM64_ADRP_LDR_GOT:
+ LOH_ASSERT(alt.info.count == 1);
+ LOH_ASSERT(isPageKind(infoA.fixup, true));
+ LOH_ASSERT(isPageOffsetKind(infoB.fixup, true));
+ LOH_ASSERT(infoA.target == infoB.target);
+ LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
+ isADRP = parseADRP(infoA.instruction, adrpInfoA);
+ isADD = parseADD(infoB.instruction, addInfoB);
+ isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB);
+ usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
+ if ( isADRP ) {
+ if ( isLDR ) {
+ if ( usableSegment && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) {
+ // can do T5 transform (LDR literal load of GOT)
+ set32LE(infoA.instructionContent, makeNOP());
+ set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress));
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got at 0x%08llX T5 transformed to NOP/LDR\n", infoC.instructionAddress);
+ }
+ }
+ }
+ else if ( isADD ) {
+ if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress) ) {
+ // can do T4 transform (ADR to compute local address)
+ set32LE(infoA.instructionContent, makeADR(addInfoB.destReg, infoA.targetAddress, infoA.instructionAddress));
+ set32LE(infoB.instructionContent, makeNOP());
+ if ( _options.verboseOptimizationHints() ) {
+ fprintf(stderr, "adrp-ldr-got at 0x%08llX T4 transformed to ADR/STR\n", infoC.instructionAddress);
+ }
+ }
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr-got at 0x%08llX not LDR or ADD\n", infoB.instructionAddress);
+ }
+ }
+ else {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "adrp-ldr-got at 0x%08llX not ADRP\n", infoA.instructionAddress);
+ }
+ break;
+ default:
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "unknown hint kind %d alt.info.kind at 0x%08llX\n", alt.info.kind, infoA.instructionAddress);
+ break;
+ }
+ }
+ // apply hints pass 2
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
+ if ( fit->kind != ld::Fixup::kindLinkerOptimizationHint )
+ continue;
+ InstructionInfo infoA;
+ InstructionInfo infoB;
+ ld::Fixup::LOH_arm64 alt;
+ alt.addend = fit->u.addend;
+ setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta1 << 2), &infoA);
+ if ( alt.info.count > 0 )
+ setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta2 << 2), &infoB);
+
+ switch ( alt.info.kind ) {
+ case LOH_ARM64_ADRP_ADRP:
+ LOH_ASSERT(isPageKind(infoA.fixup));
+ LOH_ASSERT(isPageKind(infoB.fixup));
+ if ( (infoA.instruction & 0x9F000000) != 0x90000000 ) {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "may-reused-adrp at 0x%08llX no longer an ADRP, now 0x%08X\n", infoA.instructionAddress, infoA.instruction);
+ sAdrpNA++;
+ break;
+ }
+ if ( (infoB.instruction & 0x9F000000) != 0x90000000 ) {
+ if ( _options.verboseOptimizationHints() )
+ fprintf(stderr, "may-reused-adrp at 0x%08llX no longer an ADRP, now 0x%08X\n", infoB.instructionAddress, infoA.instruction);
+ sAdrpNA++;
+ break;
+ }
+ if ( (infoA.targetAddress & (-4096)) == (infoB.targetAddress & (-4096)) ) {
+ set32LE(infoB.instructionContent, 0xD503201F);
+ sAdrpNoped++;
+ }
+ else {
+ sAdrpNotNoped++;
+ }
+ break;
+ }