loongarch.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. #pragma once
  2. #ifdef __GNUC__
  3. #pragma GCC diagnostic push
  4. #pragma GCC diagnostic ignored "-Wunused-parameter"
  5. #endif
  6. //= loongarch.h - Generic JITLink loongarch edge kinds, utilities -*- C++ -*-=//
  7. //
  8. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  9. // See https://llvm.org/LICENSE.txt for license information.
  10. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  11. //
  12. //===----------------------------------------------------------------------===//
  13. //
  14. // Generic utilities for graphs representing loongarch objects.
  15. //
  16. //===----------------------------------------------------------------------===//
  17. #ifndef LLVM_EXECUTIONENGINE_JITLINK_LOONGARCH_H
  18. #define LLVM_EXECUTIONENGINE_JITLINK_LOONGARCH_H
  19. #include "TableManager.h"
  20. #include "llvm/ExecutionEngine/JITLink/JITLink.h"
  21. #include "llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h"
  22. namespace llvm {
  23. namespace jitlink {
  24. namespace loongarch {
  25. /// Represents loongarch fixups.
  26. enum EdgeKind_loongarch : Edge::Kind {
  27. /// A plain 64-bit pointer value relocation.
  28. ///
  29. /// Fixup expression:
  30. /// Fixup <- Target + Addend : uint64
  31. ///
  32. Pointer64 = Edge::FirstRelocation,
  33. /// A plain 32-bit pointer value relocation.
  34. ///
  35. /// Fixup expression:
  36. /// Fixup <- Target + Addend : uint32
  37. ///
  38. /// Errors:
  39. /// - The target must reside in the low 32-bits of the address space,
  40. /// otherwise an out-of-range error will be returned.
  41. ///
  42. Pointer32,
  43. /// A 26-bit PC-relative branch.
  44. ///
  45. /// Represents a PC-relative call or branch to a target within +/-128Mb. The
  46. /// target must be 4-byte aligned.
  47. ///
  48. /// Fixup expression:
  49. /// Fixup <- (Target - Fixup + Addend) >> 2 : int26
  50. ///
  51. /// Notes:
  52. /// The '26' in the name refers to the number operand bits and follows the
  53. /// naming convention used by the corresponding ELF relocations. Since the low
  54. /// two bits must be zero (because of the 4-byte alignment of the target) the
  55. /// operand is effectively a signed 28-bit number.
  56. ///
  57. /// Errors:
  58. /// - The result of the unshifted part of the fixup expression must be
  59. /// 4-byte aligned otherwise an alignment error will be returned.
  60. /// - The result of the fixup expression must fit into an int26 otherwise an
  61. /// out-of-range error will be returned.
  62. ///
  63. Branch26PCRel,
  64. /// A 32-bit delta.
  65. ///
  66. /// Delta from the fixup to the target.
  67. ///
  68. /// Fixup expression:
  69. /// Fixup <- Target - Fixup + Addend : int32
  70. ///
  71. /// Errors:
  72. /// - The result of the fixup expression must fit into an int32, otherwise
  73. /// an out-of-range error will be returned.
  74. ///
  75. Delta32,
  76. /// A 32-bit negative delta.
  77. ///
  78. /// Delta from the target back to the fixup.
  79. ///
  80. /// Fixup expression:
  81. /// Fixup <- Fixup - Target + Addend : int32
  82. ///
  83. /// Errors:
  84. /// - The result of the fixup expression must fit into an int32, otherwise
  85. /// an out-of-range error will be returned.
  86. ///
  87. NegDelta32,
  88. /// A 64-bit delta.
  89. ///
  90. /// Delta from the fixup to the target.
  91. ///
  92. /// Fixup expression:
  93. /// Fixup <- Target - Fixup + Addend : int64
  94. ///
  95. Delta64,
  96. /// The signed 20-bit delta from the fixup page to the page containing the
  97. /// target.
  98. ///
  99. /// Fixup expression:
  100. /// Fixup <- (((Target + Addend + ((Target + Addend) & 0x800)) & ~0xfff)
  101. // - (Fixup & ~0xfff)) >> 12 : int20
  102. ///
  103. /// Notes:
  104. /// For PCALAU12I fixups.
  105. ///
  106. /// Errors:
  107. /// - The result of the fixup expression must fit into an int20 otherwise an
  108. /// out-of-range error will be returned.
  109. ///
  110. Page20,
  111. /// The 12-bit offset of the target within its page.
  112. ///
  113. /// Typically used to fix up ADDI/LD_W/LD_D immediates.
  114. ///
  115. /// Fixup expression:
  116. /// Fixup <- ((Target + Addend) >> Shift) & 0xfff : int12
  117. ///
  118. PageOffset12,
  119. /// A GOT entry getter/constructor, transformed to Page20 pointing at the GOT
  120. /// entry for the original target.
  121. ///
  122. /// Indicates that this edge should be transformed into a Page20 targeting
  123. /// the GOT entry for the edge's current target, maintaining the same addend.
  124. /// A GOT entry for the target should be created if one does not already
  125. /// exist.
  126. ///
  127. /// Edges of this kind are usually handled by a GOT/PLT builder pass inserted
  128. /// by default.
  129. ///
  130. /// Fixup expression:
  131. /// NONE
  132. ///
  133. /// Errors:
  134. /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup
  135. /// phase will result in an assert/unreachable during the fixup phase.
  136. ///
  137. RequestGOTAndTransformToPage20,
  138. /// A GOT entry getter/constructor, transformed to Pageoffset12 pointing at
  139. /// the GOT entry for the original target.
  140. ///
  141. /// Indicates that this edge should be transformed into a PageOffset12
  142. /// targeting the GOT entry for the edge's current target, maintaining the
  143. /// same addend. A GOT entry for the target should be created if one does not
  144. /// already exist.
  145. ///
  146. /// Edges of this kind are usually handled by a GOT/PLT builder pass inserted
  147. /// by default.
  148. ///
  149. /// Fixup expression:
  150. /// NONE
  151. ///
  152. RequestGOTAndTransformToPageOffset12,
  153. };
  154. /// Returns a string name for the given loongarch edge. For debugging purposes
  155. /// only.
  156. const char *getEdgeKindName(Edge::Kind K);
  157. // Returns extract bits Val[Hi:Lo].
  158. inline uint32_t extractBits(uint32_t Val, unsigned Hi, unsigned Lo) {
  159. return (Val & (((1UL << (Hi + 1)) - 1))) >> Lo;
  160. }
  161. /// Apply fixup expression for edge to block content.
  162. inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
  163. using namespace support;
  164. char *BlockWorkingMem = B.getAlreadyMutableContent().data();
  165. char *FixupPtr = BlockWorkingMem + E.getOffset();
  166. uint64_t FixupAddress = (B.getAddress() + E.getOffset()).getValue();
  167. uint64_t TargetAddress = E.getTarget().getAddress().getValue();
  168. int64_t Addend = E.getAddend();
  169. switch (E.getKind()) {
  170. case Pointer64:
  171. *(ulittle64_t *)FixupPtr = TargetAddress + Addend;
  172. break;
  173. case Pointer32: {
  174. uint64_t Value = TargetAddress + Addend;
  175. if (Value > std::numeric_limits<uint32_t>::max())
  176. return makeTargetOutOfRangeError(G, B, E);
  177. *(ulittle32_t *)FixupPtr = Value;
  178. break;
  179. }
  180. case Branch26PCRel: {
  181. int64_t Value = TargetAddress - FixupAddress + Addend;
  182. if (!isInt<28>(Value))
  183. return makeTargetOutOfRangeError(G, B, E);
  184. if (!isShiftedInt<26, 2>(Value))
  185. return makeAlignmentError(orc::ExecutorAddr(FixupAddress), Value, 4, E);
  186. uint32_t RawInstr = *(little32_t *)FixupPtr;
  187. uint32_t Imm = static_cast<uint32_t>(Value >> 2);
  188. uint32_t Imm15_0 = extractBits(Imm, /*Hi=*/15, /*Lo=*/0) << 10;
  189. uint32_t Imm25_16 = extractBits(Imm, /*Hi=*/25, /*Lo=*/16);
  190. *(little32_t *)FixupPtr = RawInstr | Imm15_0 | Imm25_16;
  191. break;
  192. }
  193. case Delta32: {
  194. int64_t Value = TargetAddress - FixupAddress + Addend;
  195. if (!isInt<32>(Value))
  196. return makeTargetOutOfRangeError(G, B, E);
  197. *(little32_t *)FixupPtr = Value;
  198. break;
  199. }
  200. case NegDelta32: {
  201. int64_t Value = FixupAddress - TargetAddress + Addend;
  202. if (!isInt<32>(Value))
  203. return makeTargetOutOfRangeError(G, B, E);
  204. *(little32_t *)FixupPtr = Value;
  205. break;
  206. }
  207. case Delta64:
  208. *(little64_t *)FixupPtr = TargetAddress - FixupAddress + Addend;
  209. break;
  210. case Page20: {
  211. uint64_t Target = TargetAddress + Addend;
  212. uint64_t TargetPage =
  213. (Target + (Target & 0x800)) & ~static_cast<uint64_t>(0xfff);
  214. uint64_t PCPage = FixupAddress & ~static_cast<uint64_t>(0xfff);
  215. int64_t PageDelta = TargetPage - PCPage;
  216. if (!isInt<32>(PageDelta))
  217. return makeTargetOutOfRangeError(G, B, E);
  218. uint32_t RawInstr = *(little32_t *)FixupPtr;
  219. uint32_t Imm31_12 = extractBits(PageDelta, /*Hi=*/31, /*Lo=*/12) << 5;
  220. *(little32_t *)FixupPtr = RawInstr | Imm31_12;
  221. break;
  222. }
  223. case PageOffset12: {
  224. uint64_t TargetOffset = (TargetAddress + Addend) & 0xfff;
  225. uint32_t RawInstr = *(ulittle32_t *)FixupPtr;
  226. uint32_t Imm11_0 = TargetOffset << 10;
  227. *(ulittle32_t *)FixupPtr = RawInstr | Imm11_0;
  228. break;
  229. }
  230. default:
  231. return make_error<JITLinkError>(
  232. "In graph " + G.getName() + ", section " + B.getSection().getName() +
  233. " unsupported edge kind " + getEdgeKindName(E.getKind()));
  234. }
  235. return Error::success();
  236. }
  237. /// loongarch null pointer content.
  238. extern const char NullPointerContent[8];
  239. inline ArrayRef<char> getGOTEntryBlockContent(LinkGraph &G) {
  240. return {reinterpret_cast<const char *>(NullPointerContent),
  241. G.getPointerSize()};
  242. }
  243. /// loongarch stub content.
  244. ///
  245. /// Contains the instruction sequence for an indirect jump via an in-memory
  246. /// pointer:
  247. /// pcalau12i $t8, %page20(ptr)
  248. /// ld.[w/d] $t8, %pageoff12(ptr)
  249. /// jr $t8
  250. constexpr size_t StubEntrySize = 12;
  251. extern const uint8_t LA64StubContent[StubEntrySize];
  252. extern const uint8_t LA32StubContent[StubEntrySize];
  253. inline ArrayRef<char> getStubBlockContent(LinkGraph &G) {
  254. auto StubContent =
  255. G.getPointerSize() == 8 ? LA64StubContent : LA32StubContent;
  256. return {reinterpret_cast<const char *>(StubContent), StubEntrySize};
  257. }
  258. /// Creates a new pointer block in the given section and returns an
  259. /// Anonymous symobl pointing to it.
  260. ///
  261. /// If InitialTarget is given then an Pointer64 relocation will be added to the
  262. /// block pointing at InitialTarget.
  263. ///
  264. /// The pointer block will have the following default values:
  265. /// alignment: PointerSize
  266. /// alignment-offset: 0
  267. inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection,
  268. Symbol *InitialTarget = nullptr,
  269. uint64_t InitialAddend = 0) {
  270. auto &B = G.createContentBlock(PointerSection, getGOTEntryBlockContent(G),
  271. orc::ExecutorAddr(), G.getPointerSize(), 0);
  272. if (InitialTarget)
  273. B.addEdge(G.getPointerSize() == 8 ? Pointer64 : Pointer32, 0,
  274. *InitialTarget, InitialAddend);
  275. return G.addAnonymousSymbol(B, 0, G.getPointerSize(), false, false);
  276. }
  277. /// Create a jump stub that jumps via the pointer at the given symbol and
  278. /// an anonymous symbol pointing to it. Return the anonymous symbol.
  279. inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G,
  280. Section &StubSection,
  281. Symbol &PointerSymbol) {
  282. Block &StubContentBlock = G.createContentBlock(
  283. StubSection, getStubBlockContent(G), orc::ExecutorAddr(), 4, 0);
  284. StubContentBlock.addEdge(Page20, 0, PointerSymbol, 0);
  285. StubContentBlock.addEdge(PageOffset12, 4, PointerSymbol, 0);
  286. return G.addAnonymousSymbol(StubContentBlock, 0, StubEntrySize, true, false);
  287. }
  288. /// Global Offset Table Builder.
  289. class GOTTableManager : public TableManager<GOTTableManager> {
  290. public:
  291. static StringRef getSectionName() { return "$__GOT"; }
  292. bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
  293. Edge::Kind KindToSet = Edge::Invalid;
  294. switch (E.getKind()) {
  295. case RequestGOTAndTransformToPage20:
  296. KindToSet = Page20;
  297. break;
  298. case RequestGOTAndTransformToPageOffset12:
  299. KindToSet = PageOffset12;
  300. break;
  301. default:
  302. return false;
  303. }
  304. assert(KindToSet != Edge::Invalid &&
  305. "Fell through switch, but no new kind to set");
  306. DEBUG_WITH_TYPE("jitlink", {
  307. dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
  308. << B->getFixupAddress(E) << " (" << B->getAddress() << " + "
  309. << formatv("{0:x}", E.getOffset()) << ")\n";
  310. });
  311. E.setKind(KindToSet);
  312. E.setTarget(getEntryForTarget(G, E.getTarget()));
  313. return true;
  314. }
  315. Symbol &createEntry(LinkGraph &G, Symbol &Target) {
  316. return createAnonymousPointer(G, getGOTSection(G), &Target);
  317. }
  318. private:
  319. Section &getGOTSection(LinkGraph &G) {
  320. if (!GOTSection)
  321. GOTSection = &G.createSection(getSectionName(),
  322. orc::MemProt::Read | orc::MemProt::Exec);
  323. return *GOTSection;
  324. }
  325. Section *GOTSection = nullptr;
  326. };
  327. /// Procedure Linkage Table Builder.
  328. class PLTTableManager : public TableManager<PLTTableManager> {
  329. public:
  330. PLTTableManager(GOTTableManager &GOT) : GOT(GOT) {}
  331. static StringRef getSectionName() { return "$__STUBS"; }
  332. bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
  333. if (E.getKind() == Branch26PCRel && !E.getTarget().isDefined()) {
  334. DEBUG_WITH_TYPE("jitlink", {
  335. dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
  336. << B->getFixupAddress(E) << " (" << B->getAddress() << " + "
  337. << formatv("{0:x}", E.getOffset()) << ")\n";
  338. });
  339. E.setTarget(getEntryForTarget(G, E.getTarget()));
  340. return true;
  341. }
  342. return false;
  343. }
  344. Symbol &createEntry(LinkGraph &G, Symbol &Target) {
  345. return createAnonymousPointerJumpStub(G, getStubsSection(G),
  346. GOT.getEntryForTarget(G, Target));
  347. }
  348. public:
  349. Section &getStubsSection(LinkGraph &G) {
  350. if (!StubsSection)
  351. StubsSection = &G.createSection(getSectionName(),
  352. orc::MemProt::Read | orc::MemProt::Exec);
  353. return *StubsSection;
  354. }
  355. GOTTableManager &GOT;
  356. Section *StubsSection = nullptr;
  357. };
  358. } // namespace loongarch
  359. } // namespace jitlink
  360. } // namespace llvm
  361. #endif
  362. #ifdef __GNUC__
  363. #pragma GCC diagnostic pop
  364. #endif