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