1 //===-- x86_64.h - Generic JITLink x86-64 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 x86-64 objects. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef LLVM_EXECUTIONENGINE_JITLINK_X86_64_H 14 #define LLVM_EXECUTIONENGINE_JITLINK_X86_64_H 15 16 #include "llvm/ExecutionEngine/JITLink/JITLink.h" 17 #include "llvm/ExecutionEngine/JITLink/TableManager.h" 18 19 #include <limits> 20 21 namespace llvm { 22 namespace jitlink { 23 namespace x86_64 { 24 25 /// Represents x86-64 fixups and other x86-64-specific edge kinds. 26 enum EdgeKind_x86_64 : Edge::Kind { 27 28 /// A plain 64-bit pointer value relocation. 29 /// 30 /// Fixup expression: 31 /// Fixup <- Target + Addend : uint64 32 /// 33 Pointer64 = Edge::FirstRelocation, 34 35 /// A plain 32-bit pointer value relocation. 36 /// 37 /// Fixup expression: 38 /// Fixup <- Target + Addend : uint32 39 /// 40 /// Errors: 41 /// - The target must reside in the low 32-bits of the address space, 42 /// otherwise an out-of-range error will be returned. 43 /// 44 Pointer32, 45 46 /// A signed 32-bit pointer value relocation 47 /// 48 /// Fixup expression: 49 /// Fixup <- Target + Addend : int32 50 /// 51 /// Errors: 52 /// - The target must reside in the signed 32-bits([-2**31, 2**32 - 1]) of 53 /// the address space, otherwise an out-of-range error will be returned. 54 Pointer32Signed, 55 56 /// A 64-bit delta. 57 /// 58 /// Delta from the fixup to the target. 59 /// 60 /// Fixup expression: 61 /// Fixup <- Target - Fixup + Addend : int64 62 /// 63 Delta64, 64 65 /// A 32-bit delta. 66 /// 67 /// Delta from the fixup to the target. 68 /// 69 /// Fixup expression: 70 /// Fixup <- Target - Fixup + Addend : int64 71 /// 72 /// Errors: 73 /// - The result of the fixup expression must fit into an int32, otherwise 74 /// an out-of-range error will be returned. 75 /// 76 Delta32, 77 78 /// A 64-bit negative delta. 79 /// 80 /// Delta from target back to the fixup. 81 /// 82 /// Fixup expression: 83 /// Fixup <- Fixup - Target + Addend : int64 84 /// 85 NegDelta64, 86 87 /// A 32-bit negative delta. 88 /// 89 /// Delta from the target back to the fixup. 90 /// 91 /// Fixup expression: 92 /// Fixup <- Fixup - Target + Addend : int32 93 /// 94 /// Errors: 95 /// - The result of the fixup expression must fit into an int32, otherwise 96 /// an out-of-range error will be returned. 97 NegDelta32, 98 99 /// A 64-bit GOT delta. 100 /// 101 /// Delta from the global offset table to the target 102 /// 103 /// Fixup expression: 104 /// Fixup <- Target - GOTSymbol + Addend : int64 105 /// 106 /// Errors: 107 /// - *ASSERTION* Failure to a null pointer GOTSymbol, which the GOT section 108 /// symbol was not been defined. 109 Delta64FromGOT, 110 111 /// A 32-bit PC-relative branch. 112 /// 113 /// Represents a PC-relative call or branch to a target. This can be used to 114 /// identify, record, and/or patch call sites. 115 /// 116 /// The fixup expression for this kind includes an implicit offset to account 117 /// for the PC (unlike the Delta edges) so that a Branch32PCRel with a target 118 /// T and addend zero is a call/branch to the start (offset zero) of T. 119 /// 120 /// Fixup expression: 121 /// Fixup <- Target - (Fixup + 4) + Addend : int32 122 /// 123 /// Errors: 124 /// - The result of the fixup expression must fit into an int32, otherwise 125 /// an out-of-range error will be returned. 126 /// 127 BranchPCRel32, 128 129 /// A 32-bit PC-relative relocation. 130 /// 131 /// Represents a data/control flow instruction using PC-relative addressing 132 /// to a target. 133 /// 134 /// The fixup expression for this kind includes an implicit offset to account 135 /// for the PC (unlike the Delta edges) so that a PCRel32 with a target 136 /// T and addend zero is a call/branch to the start (offset zero) of T. 137 /// 138 /// Fixup expression: 139 /// Fixup <- Target - (Fixup + 4) + Addend : int32 140 /// 141 /// Errors: 142 /// - The result of the fixup expression must fit into an int32, otherwise 143 /// an out-of-range error will be returned. 144 /// 145 PCRel32, 146 147 /// A 32-bit PC-relative branch to a pointer jump stub. 148 /// 149 /// The target of this relocation should be a pointer jump stub of the form: 150 /// 151 /// \code{.s} 152 /// .text 153 /// jmpq *tgtptr(%rip) 154 /// ; ... 155 /// 156 /// .data 157 /// tgtptr: 158 /// .quad 0 159 /// \endcode 160 /// 161 /// This edge kind has the same fixup expression as BranchPCRel32, but further 162 /// identifies the call/branch as being to a pointer jump stub. For edges of 163 /// this kind the jump stub should not be bypassed (use 164 /// BranchPCRel32ToPtrJumpStubBypassable for that), but the pointer location 165 /// target may be recorded to allow manipulation at runtime. 166 /// 167 /// Fixup expression: 168 /// Fixup <- Target - Fixup + Addend - 4 : int32 169 /// 170 /// Errors: 171 /// - The result of the fixup expression must fit into an int32, otherwise 172 /// an out-of-range error will be returned. 173 /// 174 BranchPCRel32ToPtrJumpStub, 175 176 /// A relaxable version of BranchPCRel32ToPtrJumpStub. 177 /// 178 /// The edge kind has the same fixup expression as BranchPCRel32ToPtrJumpStub, 179 /// but identifies the call/branch as being to a pointer jump stub that may be 180 /// bypassed with a direct jump to the ultimate target if the ultimate target 181 /// is within range of the fixup location. 182 /// 183 /// Fixup expression: 184 /// Fixup <- Target - Fixup + Addend - 4: int32 185 /// 186 /// Errors: 187 /// - The result of the fixup expression must fit into an int32, otherwise 188 /// an out-of-range error will be returned. 189 /// 190 BranchPCRel32ToPtrJumpStubBypassable, 191 192 /// A GOT entry getter/constructor, transformed to Delta32 pointing at the GOT 193 /// entry for the original target. 194 /// 195 /// Indicates that this edge should be transformed into a Delta32 targeting 196 /// the GOT entry for the edge's current target, maintaining the same addend. 197 /// A GOT entry for the target should be created if one does not already 198 /// exist. 199 /// 200 /// Edges of this kind are usually handled by a GOT builder pass inserted by 201 /// default. 202 /// 203 /// Fixup expression: 204 /// NONE 205 /// 206 /// Errors: 207 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup 208 /// phase will result in an assert/unreachable during the fixup phase. 209 /// 210 RequestGOTAndTransformToDelta32, 211 212 /// A GOT entry getter/constructor, transformed to Delta64 pointing at the GOT 213 /// entry for the original target. 214 /// 215 /// Indicates that this edge should be transformed into a Delta64 targeting 216 /// the GOT entry for the edge's current target, maintaining the same addend. 217 /// A GOT entry for the target should be created if one does not already 218 /// exist. 219 /// 220 /// Edges of this kind are usually handled by a GOT builder pass inserted by 221 /// default. 222 /// 223 /// Fixup expression: 224 /// NONE 225 /// 226 /// Errors: 227 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup 228 /// phase will result in an assert/unreachable during the fixup phase. 229 /// 230 RequestGOTAndTransformToDelta64, 231 232 /// A GOT entry offset within GOT getter/constructor, transformed to 233 /// Delta64FromGOT 234 /// pointing at the GOT entry for the original target 235 /// 236 /// Indicates that this edge should be transformed into a Delta64FromGOT 237 /// targeting 238 /// the GOT entry for the edge's current target, maintaining the same addend. 239 /// A GOT entry for the target should be created if one does not already 240 /// exist. 241 /// 242 /// Edges of this kind are usually handled by a GOT builder pass inserted by 243 /// default 244 /// 245 /// Fixup expression: 246 /// NONE 247 /// 248 /// Errors: 249 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup 250 /// phase will result in an assert/unreachable during the fixup phase 251 RequestGOTAndTransformToDelta64FromGOT, 252 253 /// A PC-relative load of a GOT entry, relaxable if GOT entry target is 254 /// in-range of the fixup 255 /// 256 /// TODO: Explain the optimization 257 /// 258 /// Fixup expression 259 /// Fixup <- Target - (Fixup + 4) + Addend : int32 260 /// 261 /// Errors: 262 /// - The result of the fixup expression must fit into an int32, otherwise 263 /// an out-of-range error will be returned. 264 // 265 PCRel32GOTLoadRelaxable, 266 267 /// A PC-relative REX load of a GOT entry, relaxable if GOT entry target 268 /// is in-range of the fixup. 269 /// 270 /// If the GOT entry target is in-range of the fixup then the load from the 271 /// GOT may be replaced with a direct memory address calculation. 272 /// 273 /// Fixup expression: 274 /// Fixup <- Target - (Fixup + 4) + Addend : int32 275 /// 276 /// Errors: 277 /// - The result of the fixup expression must fit into an int32, otherwise 278 /// an out-of-range error will be returned. 279 /// 280 PCRel32GOTLoadREXRelaxable, 281 282 /// A GOT entry getter/constructor, transformed to 283 /// PCRel32ToGOTLoadREXRelaxable pointing at the GOT entry for the original 284 /// target. 285 /// 286 /// Indicates that this edge should be lowered to a PC32ToGOTLoadREXRelaxable 287 /// targeting the GOT entry for the edge's current target, maintaining the 288 /// same addend. A GOT entry for the target should be created if one does not 289 /// already exist. 290 /// 291 /// Edges of this kind are usually lowered by a GOT builder pass inserted by 292 /// default. 293 /// 294 /// Fixup expression: 295 /// NONE 296 /// 297 /// Errors: 298 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup 299 /// phase will result in an assert/unreachable during the fixup phase. 300 /// 301 RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable, 302 303 /// A GOT entry getter/constructor, transformed to 304 /// PCRel32ToGOTLoadRelaxable pointing at the GOT entry for the original 305 /// target. 306 /// 307 /// Indicates that this edge should be lowered to a PC32ToGOTLoadRelaxable 308 /// targeting the GOT entry for the edge's current target, maintaining the 309 /// same addend. A GOT entry for the target should be created if one does not 310 /// already exist. 311 /// 312 /// Edges of this kind are usually lowered by a GOT builder pass inserted by 313 /// default. 314 /// 315 /// Fixup expression: 316 /// NONE 317 /// 318 /// Errors: 319 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup 320 /// phase will result in an assert/unreachable during the fixup phase. 321 /// 322 RequestGOTAndTransformToPCRel32GOTLoadRelaxable, 323 324 /// A PC-relative REX load of a Thread Local Variable Pointer (TLVP) entry, 325 /// relaxable if the TLVP entry target is in-range of the fixup. 326 /// 327 /// If the TLVP entry target is in-range of the fixup then the load from the 328 /// TLVP may be replaced with a direct memory address calculation. 329 /// 330 /// The target of this edge must be a thread local variable entry of the form 331 /// .quad <tlv getter thunk> 332 /// .quad <tlv key> 333 /// .quad <tlv initializer> 334 /// 335 /// Fixup expression: 336 /// Fixup <- Target - (Fixup + 4) + Addend : int32 337 /// 338 /// Errors: 339 /// - The result of the fixup expression must fit into an int32, otherwise 340 /// an out-of-range error will be returned. 341 /// - The target must be either external, or a TLV entry of the required 342 /// form, otherwise a malformed TLV entry error will be returned. 343 /// 344 PCRel32TLVPLoadREXRelaxable, 345 346 /// TODO: Explain the generic edge kind 347 RequestTLSDescInGOTAndTransformToDelta32, 348 349 /// A TLVP entry getter/constructor, transformed to 350 /// Delta32ToTLVPLoadREXRelaxable. 351 /// 352 /// Indicates that this edge should be transformed into a 353 /// Delta32ToTLVPLoadREXRelaxable targeting the TLVP entry for the edge's 354 /// current target. A TLVP entry for the target should be created if one does 355 /// not already exist. 356 /// 357 /// Fixup expression: 358 /// NONE 359 /// 360 /// Errors: 361 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup 362 /// phase will result in an assert/unreachable during the fixup phase. 363 /// 364 RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable, 365 // First platform specific relocation. 366 FirstPlatformRelocation 367 }; 368 369 /// Returns a string name for the given x86-64 edge. For debugging purposes 370 /// only. 371 const char *getEdgeKindName(Edge::Kind K); 372 373 /// Returns true if the given uint64_t value is in range for a uint32_t. 374 inline bool isInRangeForImmU32(uint64_t Value) { 375 return Value <= std::numeric_limits<uint32_t>::max(); 376 } 377 378 /// Returns true if the given int64_t value is in range for an int32_t. 379 inline bool isInRangeForImmS32(int64_t Value) { 380 return (Value >= std::numeric_limits<int32_t>::min() && 381 Value <= std::numeric_limits<int32_t>::max()); 382 } 383 384 /// Apply fixup expression for edge to block content. 385 inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E, 386 const Symbol *GOTSymbol) { 387 using namespace support; 388 389 char *BlockWorkingMem = B.getAlreadyMutableContent().data(); 390 char *FixupPtr = BlockWorkingMem + E.getOffset(); 391 auto FixupAddress = B.getAddress() + E.getOffset(); 392 393 switch (E.getKind()) { 394 395 case Pointer64: { 396 uint64_t Value = E.getTarget().getAddress().getValue() + E.getAddend(); 397 *(ulittle64_t *)FixupPtr = Value; 398 break; 399 } 400 401 case Pointer32: { 402 uint64_t Value = E.getTarget().getAddress().getValue() + E.getAddend(); 403 if (LLVM_LIKELY(isInRangeForImmU32(Value))) 404 *(ulittle32_t *)FixupPtr = Value; 405 else 406 return makeTargetOutOfRangeError(G, B, E); 407 break; 408 } 409 case Pointer32Signed: { 410 int64_t Value = E.getTarget().getAddress().getValue() + E.getAddend(); 411 if (LLVM_LIKELY(isInRangeForImmS32(Value))) 412 *(little32_t *)FixupPtr = Value; 413 else 414 return makeTargetOutOfRangeError(G, B, E); 415 break; 416 } 417 418 case PCRel32: 419 case BranchPCRel32: 420 case BranchPCRel32ToPtrJumpStub: 421 case BranchPCRel32ToPtrJumpStubBypassable: 422 case PCRel32GOTLoadRelaxable: 423 case PCRel32GOTLoadREXRelaxable: 424 case PCRel32TLVPLoadREXRelaxable: { 425 int64_t Value = 426 E.getTarget().getAddress() - (FixupAddress + 4) + E.getAddend(); 427 if (LLVM_LIKELY(isInRangeForImmS32(Value))) 428 *(little32_t *)FixupPtr = Value; 429 else 430 return makeTargetOutOfRangeError(G, B, E); 431 break; 432 } 433 434 case Delta64: { 435 int64_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend(); 436 *(little64_t *)FixupPtr = Value; 437 break; 438 } 439 440 case Delta32: { 441 int64_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend(); 442 if (LLVM_LIKELY(isInRangeForImmS32(Value))) 443 *(little32_t *)FixupPtr = Value; 444 else 445 return makeTargetOutOfRangeError(G, B, E); 446 break; 447 } 448 449 case NegDelta64: { 450 int64_t Value = FixupAddress - E.getTarget().getAddress() + E.getAddend(); 451 *(little64_t *)FixupPtr = Value; 452 break; 453 } 454 455 case NegDelta32: { 456 int64_t Value = FixupAddress - E.getTarget().getAddress() + E.getAddend(); 457 if (LLVM_LIKELY(isInRangeForImmS32(Value))) 458 *(little32_t *)FixupPtr = Value; 459 else 460 return makeTargetOutOfRangeError(G, B, E); 461 break; 462 } 463 case Delta64FromGOT: { 464 assert(GOTSymbol && "No GOT section symbol"); 465 int64_t Value = 466 E.getTarget().getAddress() - GOTSymbol->getAddress() + E.getAddend(); 467 *(little64_t *)FixupPtr = Value; 468 break; 469 } 470 471 default: 472 return make_error<JITLinkError>( 473 "In graph " + G.getName() + ", section " + B.getSection().getName() + 474 "unsupported edge kind" + getEdgeKindName(E.getKind())); 475 } 476 477 return Error::success(); 478 } 479 480 /// x86_64 pointer size. 481 constexpr uint64_t PointerSize = 8; 482 483 /// x86-64 null pointer content. 484 extern const char NullPointerContent[PointerSize]; 485 486 /// x86-64 pointer jump stub content. 487 /// 488 /// Contains the instruction sequence for an indirect jump via an in-memory 489 /// pointer: 490 /// jmpq *ptr(%rip) 491 extern const char PointerJumpStubContent[6]; 492 493 /// Creates a new pointer block in the given section and returns an anonymous 494 /// symbol pointing to it. 495 /// 496 /// If InitialTarget is given then an Pointer64 relocation will be added to the 497 /// block pointing at InitialTarget. 498 /// 499 /// The pointer block will have the following default values: 500 /// alignment: 64-bit 501 /// alignment-offset: 0 502 /// address: highest allowable (~7U) 503 inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection, 504 Symbol *InitialTarget = nullptr, 505 uint64_t InitialAddend = 0) { 506 auto &B = G.createContentBlock(PointerSection, NullPointerContent, 507 orc::ExecutorAddr(~uint64_t(7)), 8, 0); 508 if (InitialTarget) 509 B.addEdge(Pointer64, 0, *InitialTarget, InitialAddend); 510 return G.addAnonymousSymbol(B, 0, 8, false, false); 511 } 512 513 /// Create a jump stub block that jumps via the pointer at the given symbol. 514 /// 515 /// The stub block will have the following default values: 516 /// alignment: 8-bit 517 /// alignment-offset: 0 518 /// address: highest allowable: (~5U) 519 inline Block &createPointerJumpStubBlock(LinkGraph &G, Section &StubSection, 520 Symbol &PointerSymbol) { 521 auto &B = G.createContentBlock(StubSection, PointerJumpStubContent, 522 orc::ExecutorAddr(~uint64_t(5)), 1, 0); 523 B.addEdge(Delta32, 2, PointerSymbol, -4); 524 return B; 525 } 526 527 /// Create a jump stub that jumps via the pointer at the given symbol and 528 /// an anonymous symbol pointing to it. Return the anonymous symbol. 529 /// 530 /// The stub block will be created by createPointerJumpStubBlock. 531 inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G, 532 Section &StubSection, 533 Symbol &PointerSymbol) { 534 return G.addAnonymousSymbol( 535 createPointerJumpStubBlock(G, StubSection, PointerSymbol), 0, 6, true, 536 false); 537 } 538 539 /// Global Offset Table Builder. 540 class GOTTableManager : public TableManager<GOTTableManager> { 541 public: 542 static StringRef getSectionName() { return "$__GOT"; } 543 544 bool visitEdge(LinkGraph &G, Block *B, Edge &E) { 545 Edge::Kind KindToSet = Edge::Invalid; 546 switch (E.getKind()) { 547 case x86_64::Delta64FromGOT: { 548 // we need to make sure that the GOT section exists, but don't otherwise 549 // need to fix up this edge 550 getGOTSection(G); 551 return false; 552 } 553 case x86_64::RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable: 554 KindToSet = x86_64::PCRel32GOTLoadREXRelaxable; 555 break; 556 case x86_64::RequestGOTAndTransformToPCRel32GOTLoadRelaxable: 557 KindToSet = x86_64::PCRel32GOTLoadRelaxable; 558 break; 559 case x86_64::RequestGOTAndTransformToDelta64: 560 KindToSet = x86_64::Delta64; 561 break; 562 case x86_64::RequestGOTAndTransformToDelta64FromGOT: 563 KindToSet = x86_64::Delta64FromGOT; 564 break; 565 case x86_64::RequestGOTAndTransformToDelta32: 566 KindToSet = x86_64::Delta32; 567 break; 568 default: 569 return false; 570 } 571 assert(KindToSet != Edge::Invalid && 572 "Fell through switch, but no new kind to set"); 573 DEBUG_WITH_TYPE("jitlink", { 574 dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at " 575 << B->getFixupAddress(E) << " (" << B->getAddress() << " + " 576 << formatv("{0:x}", E.getOffset()) << ")\n"; 577 }); 578 E.setKind(KindToSet); 579 E.setTarget(getEntryForTarget(G, E.getTarget())); 580 return true; 581 } 582 583 Symbol &createEntry(LinkGraph &G, Symbol &Target) { 584 return createAnonymousPointer(G, getGOTSection(G), &Target); 585 } 586 587 private: 588 Section &getGOTSection(LinkGraph &G) { 589 if (!GOTSection) 590 GOTSection = &G.createSection(getSectionName(), MemProt::Read); 591 return *GOTSection; 592 } 593 594 Section *GOTSection = nullptr; 595 }; 596 597 /// Procedure Linkage Table Builder. 598 class PLTTableManager : public TableManager<PLTTableManager> { 599 public: 600 PLTTableManager(GOTTableManager &GOT) : GOT(GOT) {} 601 602 static StringRef getSectionName() { return "$__STUBS"; } 603 604 bool visitEdge(LinkGraph &G, Block *B, Edge &E) { 605 if (E.getKind() == x86_64::BranchPCRel32 && !E.getTarget().isDefined()) { 606 DEBUG_WITH_TYPE("jitlink", { 607 dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at " 608 << B->getFixupAddress(E) << " (" << B->getAddress() << " + " 609 << formatv("{0:x}", E.getOffset()) << ")\n"; 610 }); 611 // Set the edge kind to Branch32ToPtrJumpStubBypassable to enable it to 612 // be optimized when the target is in-range. 613 E.setKind(x86_64::BranchPCRel32ToPtrJumpStubBypassable); 614 E.setTarget(getEntryForTarget(G, E.getTarget())); 615 return true; 616 } 617 return false; 618 } 619 620 Symbol &createEntry(LinkGraph &G, Symbol &Target) { 621 return createAnonymousPointerJumpStub(G, getStubsSection(G), 622 GOT.getEntryForTarget(G, Target)); 623 } 624 625 public: 626 Section &getStubsSection(LinkGraph &G) { 627 if (!PLTSection) 628 PLTSection = 629 &G.createSection(getSectionName(), MemProt::Read | MemProt::Exec); 630 return *PLTSection; 631 } 632 633 GOTTableManager &GOT; 634 Section *PLTSection = nullptr; 635 }; 636 637 /// Optimize the GOT and Stub relocations if the edge target address is in range 638 /// 1. PCRel32GOTLoadRelaxable. For this edge kind, if the target is in range, 639 /// then replace GOT load with lea 640 /// 2. BranchPCRel32ToPtrJumpStubRelaxable. For this edge kind, if the target is 641 /// in range, replace a indirect jump by plt stub with a direct jump to the 642 /// target 643 Error optimizeGOTAndStubAccesses(LinkGraph &G); 644 645 } // namespace x86_64 646 } // end namespace jitlink 647 } // end namespace llvm 648 649 #endif // LLVM_EXECUTIONENGINE_JITLINK_X86_64_H 650