x86_64.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. //===----- x86_64.cpp - Generic JITLink x86-64 edge kinds, utilities ------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // Generic utilities for graphs representing x86-64 objects.
  10. //
  11. //===----------------------------------------------------------------------===//
  12. #include "llvm/ExecutionEngine/JITLink/x86_64.h"
  13. #define DEBUG_TYPE "jitlink"
  14. namespace llvm {
  15. namespace jitlink {
  16. namespace x86_64 {
  17. const char *getEdgeKindName(Edge::Kind K) {
  18. switch (K) {
  19. case Pointer64:
  20. return "Pointer64";
  21. case Pointer32:
  22. return "Pointer32";
  23. case Pointer32Signed:
  24. return "Pointer32Signed";
  25. case Delta64:
  26. return "Delta64";
  27. case Delta32:
  28. return "Delta32";
  29. case NegDelta64:
  30. return "NegDelta64";
  31. case NegDelta32:
  32. return "NegDelta32";
  33. case Delta64FromGOT:
  34. return "Delta64FromGOT";
  35. case BranchPCRel32:
  36. return "BranchPCRel32";
  37. case BranchPCRel32ToPtrJumpStub:
  38. return "BranchPCRel32ToPtrJumpStub";
  39. case BranchPCRel32ToPtrJumpStubBypassable:
  40. return "BranchPCRel32ToPtrJumpStubBypassable";
  41. case RequestGOTAndTransformToDelta32:
  42. return "RequestGOTAndTransformToDelta32";
  43. case RequestGOTAndTransformToDelta64:
  44. return "RequestGOTAndTransformToDelta64";
  45. case RequestGOTAndTransformToDelta64FromGOT:
  46. return "RequestGOTAndTransformToDelta64FromGOT";
  47. case PCRel32GOTLoadREXRelaxable:
  48. return "PCRel32GOTLoadREXRelaxable";
  49. case RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable:
  50. return "RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable";
  51. case PCRel32GOTLoadRelaxable:
  52. return "PCRel32GOTLoadRelaxable";
  53. case RequestGOTAndTransformToPCRel32GOTLoadRelaxable:
  54. return "RequestGOTAndTransformToPCRel32GOTLoadRelaxable";
  55. case PCRel32TLVPLoadREXRelaxable:
  56. return "PCRel32TLVPLoadREXRelaxable";
  57. case RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable:
  58. return "RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable";
  59. default:
  60. return getGenericEdgeKindName(static_cast<Edge::Kind>(K));
  61. }
  62. }
  63. const char NullPointerContent[PointerSize] = {0x00, 0x00, 0x00, 0x00,
  64. 0x00, 0x00, 0x00, 0x00};
  65. const char PointerJumpStubContent[6] = {
  66. static_cast<char>(0xFFu), 0x25, 0x00, 0x00, 0x00, 0x00};
  67. Error optimizeGOTAndStubAccesses(LinkGraph &G) {
  68. LLVM_DEBUG(dbgs() << "Optimizing GOT entries and stubs:\n");
  69. for (auto *B : G.blocks())
  70. for (auto &E : B->edges()) {
  71. if (E.getKind() == x86_64::PCRel32GOTLoadRelaxable ||
  72. E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable) {
  73. #ifndef NDEBUG
  74. bool REXPrefix = E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable;
  75. assert(E.getOffset() >= (REXPrefix ? 3u : 2u) &&
  76. "GOT edge occurs too early in block");
  77. #endif
  78. auto *FixupData = reinterpret_cast<uint8_t *>(
  79. const_cast<char *>(B->getContent().data())) +
  80. E.getOffset();
  81. const uint8_t Op = FixupData[-2];
  82. const uint8_t ModRM = FixupData[-1];
  83. auto &GOTEntryBlock = E.getTarget().getBlock();
  84. assert(GOTEntryBlock.getSize() == G.getPointerSize() &&
  85. "GOT entry block should be pointer sized");
  86. assert(GOTEntryBlock.edges_size() == 1 &&
  87. "GOT entry should only have one outgoing edge");
  88. auto &GOTTarget = GOTEntryBlock.edges().begin()->getTarget();
  89. orc::ExecutorAddr TargetAddr = GOTTarget.getAddress();
  90. orc::ExecutorAddr EdgeAddr = B->getFixupAddress(E);
  91. int64_t Displacement = TargetAddr - EdgeAddr + 4;
  92. bool TargetInRangeForImmU32 = isInRangeForImmU32(TargetAddr.getValue());
  93. bool DisplacementInRangeForImmS32 = isInRangeForImmS32(Displacement);
  94. // If both of the Target and displacement is out of range, then
  95. // there isn't optimization chance.
  96. if (!(TargetInRangeForImmU32 || DisplacementInRangeForImmS32))
  97. continue;
  98. // Transform "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg".
  99. if (Op == 0x8b && DisplacementInRangeForImmS32) {
  100. FixupData[-2] = 0x8d;
  101. E.setKind(x86_64::Delta32);
  102. E.setTarget(GOTTarget);
  103. E.setAddend(E.getAddend() - 4);
  104. LLVM_DEBUG({
  105. dbgs() << " Replaced GOT load wih LEA:\n ";
  106. printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind()));
  107. dbgs() << "\n";
  108. });
  109. continue;
  110. }
  111. // Transform call/jmp instructions
  112. if (Op == 0xff && TargetInRangeForImmU32) {
  113. if (ModRM == 0x15) {
  114. // ABI says we can convert "call *foo@GOTPCREL(%rip)" to "nop; call
  115. // foo" But lld convert it to "addr32 call foo, because that makes
  116. // result expression to be a single instruction.
  117. FixupData[-2] = 0x67;
  118. FixupData[-1] = 0xe8;
  119. LLVM_DEBUG({
  120. dbgs() << " replaced call instruction's memory operand wih imm "
  121. "operand:\n ";
  122. printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind()));
  123. dbgs() << "\n";
  124. });
  125. } else {
  126. // Transform "jmp *foo@GOTPCREL(%rip)" to "jmp foo; nop"
  127. assert(ModRM == 0x25 && "Invalid ModRm for call/jmp instructions");
  128. FixupData[-2] = 0xe9;
  129. FixupData[3] = 0x90;
  130. E.setOffset(E.getOffset() - 1);
  131. LLVM_DEBUG({
  132. dbgs() << " replaced jmp instruction's memory operand wih imm "
  133. "operand:\n ";
  134. printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind()));
  135. dbgs() << "\n";
  136. });
  137. }
  138. E.setKind(x86_64::Pointer32);
  139. E.setTarget(GOTTarget);
  140. continue;
  141. }
  142. } else if (E.getKind() == x86_64::BranchPCRel32ToPtrJumpStubBypassable) {
  143. auto &StubBlock = E.getTarget().getBlock();
  144. assert(StubBlock.getSize() == sizeof(PointerJumpStubContent) &&
  145. "Stub block should be stub sized");
  146. assert(StubBlock.edges_size() == 1 &&
  147. "Stub block should only have one outgoing edge");
  148. auto &GOTBlock = StubBlock.edges().begin()->getTarget().getBlock();
  149. assert(GOTBlock.getSize() == G.getPointerSize() &&
  150. "GOT block should be pointer sized");
  151. assert(GOTBlock.edges_size() == 1 &&
  152. "GOT block should only have one outgoing edge");
  153. auto &GOTTarget = GOTBlock.edges().begin()->getTarget();
  154. orc::ExecutorAddr EdgeAddr = B->getAddress() + E.getOffset();
  155. orc::ExecutorAddr TargetAddr = GOTTarget.getAddress();
  156. int64_t Displacement = TargetAddr - EdgeAddr + 4;
  157. if (isInRangeForImmS32(Displacement)) {
  158. E.setKind(x86_64::BranchPCRel32);
  159. E.setTarget(GOTTarget);
  160. LLVM_DEBUG({
  161. dbgs() << " Replaced stub branch with direct branch:\n ";
  162. printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind()));
  163. dbgs() << "\n";
  164. });
  165. }
  166. }
  167. }
  168. return Error::success();
  169. }
  170. } // end namespace x86_64
  171. } // end namespace jitlink
  172. } // end namespace llvm