123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- //===----- x86_64.cpp - Generic JITLink x86-64 edge kinds, utilities ------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // Generic utilities for graphs representing x86-64 objects.
- //
- //===----------------------------------------------------------------------===//
- #include "llvm/ExecutionEngine/JITLink/x86_64.h"
- #define DEBUG_TYPE "jitlink"
- namespace llvm {
- namespace jitlink {
- namespace x86_64 {
- const char *getEdgeKindName(Edge::Kind K) {
- switch (K) {
- case Pointer64:
- return "Pointer64";
- case Pointer32:
- return "Pointer32";
- case Pointer32Signed:
- return "Pointer32Signed";
- case Delta64:
- return "Delta64";
- case Delta32:
- return "Delta32";
- case NegDelta64:
- return "NegDelta64";
- case NegDelta32:
- return "NegDelta32";
- case Delta64FromGOT:
- return "Delta64FromGOT";
- case BranchPCRel32:
- return "BranchPCRel32";
- case BranchPCRel32ToPtrJumpStub:
- return "BranchPCRel32ToPtrJumpStub";
- case BranchPCRel32ToPtrJumpStubBypassable:
- return "BranchPCRel32ToPtrJumpStubBypassable";
- case RequestGOTAndTransformToDelta32:
- return "RequestGOTAndTransformToDelta32";
- case RequestGOTAndTransformToDelta64:
- return "RequestGOTAndTransformToDelta64";
- case RequestGOTAndTransformToDelta64FromGOT:
- return "RequestGOTAndTransformToDelta64FromGOT";
- case PCRel32GOTLoadREXRelaxable:
- return "PCRel32GOTLoadREXRelaxable";
- case RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable:
- return "RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable";
- case PCRel32GOTLoadRelaxable:
- return "PCRel32GOTLoadRelaxable";
- case RequestGOTAndTransformToPCRel32GOTLoadRelaxable:
- return "RequestGOTAndTransformToPCRel32GOTLoadRelaxable";
- case PCRel32TLVPLoadREXRelaxable:
- return "PCRel32TLVPLoadREXRelaxable";
- case RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable:
- return "RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable";
- default:
- return getGenericEdgeKindName(static_cast<Edge::Kind>(K));
- }
- }
- const char NullPointerContent[PointerSize] = {0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00};
- const char PointerJumpStubContent[6] = {
- static_cast<char>(0xFFu), 0x25, 0x00, 0x00, 0x00, 0x00};
- Error optimizeGOTAndStubAccesses(LinkGraph &G) {
- LLVM_DEBUG(dbgs() << "Optimizing GOT entries and stubs:\n");
- for (auto *B : G.blocks())
- for (auto &E : B->edges()) {
- if (E.getKind() == x86_64::PCRel32GOTLoadRelaxable ||
- E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable) {
- #ifndef NDEBUG
- bool REXPrefix = E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable;
- assert(E.getOffset() >= (REXPrefix ? 3u : 2u) &&
- "GOT edge occurs too early in block");
- #endif
- auto *FixupData = reinterpret_cast<uint8_t *>(
- const_cast<char *>(B->getContent().data())) +
- E.getOffset();
- const uint8_t Op = FixupData[-2];
- const uint8_t ModRM = FixupData[-1];
- auto &GOTEntryBlock = E.getTarget().getBlock();
- assert(GOTEntryBlock.getSize() == G.getPointerSize() &&
- "GOT entry block should be pointer sized");
- assert(GOTEntryBlock.edges_size() == 1 &&
- "GOT entry should only have one outgoing edge");
- auto &GOTTarget = GOTEntryBlock.edges().begin()->getTarget();
- orc::ExecutorAddr TargetAddr = GOTTarget.getAddress();
- orc::ExecutorAddr EdgeAddr = B->getFixupAddress(E);
- int64_t Displacement = TargetAddr - EdgeAddr + 4;
- bool TargetInRangeForImmU32 = isInRangeForImmU32(TargetAddr.getValue());
- bool DisplacementInRangeForImmS32 = isInRangeForImmS32(Displacement);
- // If both of the Target and displacement is out of range, then
- // there isn't optimization chance.
- if (!(TargetInRangeForImmU32 || DisplacementInRangeForImmS32))
- continue;
- // Transform "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg".
- if (Op == 0x8b && DisplacementInRangeForImmS32) {
- FixupData[-2] = 0x8d;
- E.setKind(x86_64::Delta32);
- E.setTarget(GOTTarget);
- E.setAddend(E.getAddend() - 4);
- LLVM_DEBUG({
- dbgs() << " Replaced GOT load wih LEA:\n ";
- printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind()));
- dbgs() << "\n";
- });
- continue;
- }
- // Transform call/jmp instructions
- if (Op == 0xff && TargetInRangeForImmU32) {
- if (ModRM == 0x15) {
- // ABI says we can convert "call *foo@GOTPCREL(%rip)" to "nop; call
- // foo" But lld convert it to "addr32 call foo, because that makes
- // result expression to be a single instruction.
- FixupData[-2] = 0x67;
- FixupData[-1] = 0xe8;
- LLVM_DEBUG({
- dbgs() << " replaced call instruction's memory operand wih imm "
- "operand:\n ";
- printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind()));
- dbgs() << "\n";
- });
- } else {
- // Transform "jmp *foo@GOTPCREL(%rip)" to "jmp foo; nop"
- assert(ModRM == 0x25 && "Invalid ModRm for call/jmp instructions");
- FixupData[-2] = 0xe9;
- FixupData[3] = 0x90;
- E.setOffset(E.getOffset() - 1);
- LLVM_DEBUG({
- dbgs() << " replaced jmp instruction's memory operand wih imm "
- "operand:\n ";
- printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind()));
- dbgs() << "\n";
- });
- }
- E.setKind(x86_64::Pointer32);
- E.setTarget(GOTTarget);
- continue;
- }
- } else if (E.getKind() == x86_64::BranchPCRel32ToPtrJumpStubBypassable) {
- auto &StubBlock = E.getTarget().getBlock();
- assert(StubBlock.getSize() == sizeof(PointerJumpStubContent) &&
- "Stub block should be stub sized");
- assert(StubBlock.edges_size() == 1 &&
- "Stub block should only have one outgoing edge");
- auto &GOTBlock = StubBlock.edges().begin()->getTarget().getBlock();
- assert(GOTBlock.getSize() == G.getPointerSize() &&
- "GOT block should be pointer sized");
- assert(GOTBlock.edges_size() == 1 &&
- "GOT block should only have one outgoing edge");
- auto &GOTTarget = GOTBlock.edges().begin()->getTarget();
- orc::ExecutorAddr EdgeAddr = B->getAddress() + E.getOffset();
- orc::ExecutorAddr TargetAddr = GOTTarget.getAddress();
- int64_t Displacement = TargetAddr - EdgeAddr + 4;
- if (isInRangeForImmS32(Displacement)) {
- E.setKind(x86_64::BranchPCRel32);
- E.setTarget(GOTTarget);
- LLVM_DEBUG({
- dbgs() << " Replaced stub branch with direct branch:\n ";
- printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind()));
- dbgs() << "\n";
- });
- }
- }
- }
- return Error::success();
- }
- } // end namespace x86_64
- } // end namespace jitlink
- } // end namespace llvm
|