[JITLink][AArch64] Implement R_AARCH64_ADR_PREL_LO21
authorJob Noorman <jnoorman@igalia.com>
Fri, 9 Jun 2023 09:57:49 +0000 (11:57 +0200)
committerJob Noorman <jnoorman@igalia.com>
Fri, 9 Jun 2023 09:57:51 +0000 (11:57 +0200)
This relocation is used for the 21-bit immediate in ADR instructions.

Reviewed By: lhames

Differential Revision: https://reviews.llvm.org/D151305

llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h
llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp
llvm/lib/ExecutionEngine/JITLink/aarch64.cpp
llvm/test/ExecutionEngine/JITLink/AArch64/ELF_relocations.s

index e7e8b99..50bebf3 100644 (file)
@@ -180,6 +180,20 @@ enum EdgeKind_aarch64 : Edge::Kind {
   ///     out-of-range error will be returned.
   LDRLiteral19,
 
+  /// The signed 21-bit delta from the fixup to the target.
+  ///
+  /// Fixup expression:
+  ///
+  ///   Fixup <- Target - Fixup + Addend : int21
+  ///
+  /// Notes:
+  ///   For ADR fixups.
+  ///
+  /// Errors:
+  ///   - The result of the fixup expression must fit into an int21 otherwise an
+  ///     out-of-range error will be returned.
+  ADRLiteral21,
+
   /// The signed 21-bit delta from the fixup page to the page containing the
   /// target.
   ///
@@ -358,6 +372,11 @@ inline bool isCompAndBranchImm19(uint32_t Instr) {
   return (Instr & CompAndBranchImm19Mask) == 0x34000000;
 }
 
+inline bool isADR(uint32_t Instr) {
+  constexpr uint32_t ADRMask = 0x9f000000;
+  return (Instr & ADRMask) == 0x10000000;
+}
+
 // Returns the amount the address operand of LD/ST (imm12)
 // should be shifted right by.
 //
@@ -490,6 +509,20 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
     *(ulittle32_t *)FixupPtr = FixedInstr;
     break;
   }
+  case ADRLiteral21: {
+    assert((FixupAddress.getValue() & 0x3) == 0 && "ADR is not 32-bit aligned");
+    uint32_t RawInstr = *(ulittle32_t *)FixupPtr;
+    assert(isADR(RawInstr) && "RawInstr is not an ADR");
+    int64_t Delta = E.getTarget().getAddress() + E.getAddend() - FixupAddress;
+    if (!isInt<21>(Delta))
+      return makeTargetOutOfRangeError(G, B, E);
+    auto UDelta = static_cast<uint32_t>(Delta);
+    uint32_t EncodedImmHi = ((UDelta >> 2) & 0x7ffff) << 5;
+    uint32_t EncodedImmLo = (UDelta & 0x3) << 29;
+    uint32_t FixedInstr = RawInstr | EncodedImmHi | EncodedImmLo;
+    *(ulittle32_t *)FixupPtr = FixedInstr;
+    break;
+  }
   case TestAndBranch14PCRel: {
     assert((FixupAddress.getValue() & 0x3) == 0 &&
            "Test and branch is not 32-bit aligned");
index a062a66..fac3207 100644 (file)
@@ -47,6 +47,7 @@ class ELFLinkGraphBuilder_aarch64 : public ELFLinkGraphBuilder<ELFT> {
 private:
   enum ELFAArch64RelocationKind : Edge::Kind {
     ELFCall26 = Edge::FirstRelocation,
+    ELFAdrLo21,
     ELFAdrPage21,
     ELFAddAbs12,
     ELFLdSt8Abs12,
@@ -79,6 +80,8 @@ private:
     case ELF::R_AARCH64_CALL26:
     case ELF::R_AARCH64_JUMP26:
       return ELFCall26;
+    case ELF::R_AARCH64_ADR_PREL_LO21:
+      return ELFAdrLo21;
     case ELF::R_AARCH64_ADR_PREL_PG_HI21:
       return ELFAdrPage21;
     case ELF::R_AARCH64_ADD_ABS_LO12_NC:
@@ -186,6 +189,15 @@ private:
       Kind = aarch64::Branch26PCRel;
       break;
     }
+    case ELFAdrLo21: {
+      uint32_t Instr = *(const ulittle32_t *)FixupContent;
+      if (!aarch64::isADR(Instr))
+        return make_error<JITLinkError>(
+            "R_AARCH64_ADR_PREL_LO21 target is not an ADR instruction");
+
+      Kind = aarch64::ADRLiteral21;
+      break;
+    }
     case ELFAdrPage21: {
       Kind = aarch64::Page21;
       break;
index 785e8cc..cc58255 100644 (file)
@@ -51,6 +51,8 @@ const char *getEdgeKindName(Edge::Kind R) {
     return "TestAndBranch14PCRel";
   case CondBranch19PCRel:
     return "CondBranch19PCRel";
+  case ADRLiteral21:
+    return "ADRLiteral21";
   case Page21:
     return "Page21";
   case PageOffset12:
index e6185bf..b260961 100644 (file)
@@ -39,6 +39,18 @@ local_func_jump26:
         b      local_func
         .size   local_func_jump26, .-local_func_jump26
 
+# Check R_AARCH64_ADR_PREL_LO21 relocation of a local symbol
+#
+# jitlink-check: decode_operand(test_adr_prel_lo21, 1) = (adr_data - test_adr_prel_lo21)[20:0]
+        .globl  test_adr_prel_lo21, adr_data
+        .p2align  2
+test_adr_prel_lo21:
+        adr    x0, adr_data
+        .size test_adr_prel_lo21, .-test_adr_prel_lo21
+## ADR encoding is a bit tricky so use an offset with an irregular bit pattern
+## to test this bit better
+adr_data = test_adr_prel_lo21 + 0xe46f2
+
 # Check R_AARCH64_ADR_PREL_PG_HI21 / R_AARCH64_ADD_ABS_LO12_NC relocation of a local symbol
 #
 # For the ADR_PREL_PG_HI21/ADRP instruction we have the 21-bit delta to the 4k page