x86_64.cpp 7.3 KB

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