[llvm] [JITlink][AArch32] Implement ELF::R_ARM_MOVT_ABS and R_ARM_MOVW_ABS_NC (PR #66219)

Eymen Ünay via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 13 10:52:03 PDT 2023


https://github.com/eymay updated https://github.com/llvm/llvm-project/pull/66219:

>From 48437feb6d8b1a6e406d4543a1db00abda5b2f3e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Eymen=20=C3=9Cnay?= <eymenunay at outlook.com>
Date: Wed, 13 Sep 2023 16:51:52 +0300
Subject: [PATCH 1/2] [JITlink][AArch32] Implement ELF::R_ARM_MOVT_ABS and
 R_ARM_MOVW_ABS_NC

---
 .../llvm/ExecutionEngine/JITLink/aarch32.h    | 19 +++++-
 .../ExecutionEngine/JITLink/ELF_aarch32.cpp   |  8 +++
 llvm/lib/ExecutionEngine/JITLink/aarch32.cpp  | 67 ++++++++++++++++++-
 .../JITLink/AArch32/ELF_static_arm_reloc.s    | 30 +++++++++
 .../ExecutionEngine/JITLink/AArch32Tests.cpp  | 42 ++++++++++++
 5 files changed, 164 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
index 1c89280a10fd213..1d955c09429831c 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
@@ -55,7 +55,13 @@ enum EdgeKind_aarch32 : Edge::Kind {
   /// Write immediate value for (unconditional) PC-relative branch without link.
   Arm_Jump24,
 
-  LastArmRelocation = Arm_Jump24,
+  /// Write immediate value to the lower halfword of the destination register
+  Arm_MovwAbsNC,
+
+  /// Write immediate value to the top halfword of the destination register
+  Arm_MovtAbs,
+
+  LastArmRelocation = Arm_MovtAbs,
 
   ///
   /// Relocations of class Thumb16 and Thumb32 (covers Thumb instruction subset)
@@ -166,6 +172,17 @@ template <> struct FixupInfo<Arm_Call> : public FixupInfo<Arm_Jump24> {
   static constexpr uint32_t BitBlx = 0x10000000;
 };
 
+template <> struct FixupInfo<Arm_MovtAbs> {
+  static constexpr uint32_t Opcode = 0x03400000;
+  static constexpr uint32_t OpcodeMask = 0x0ff00000;
+  static constexpr uint32_t ImmMask = 0x000f0fff;
+  static constexpr uint32_t RegMask = 0x0000f000;
+};
+
+template <> struct FixupInfo<Arm_MovwAbsNC> : public FixupInfo<Arm_MovtAbs> {
+  static constexpr uint32_t Opcode = 0x03000000;
+};
+
 template <> struct FixupInfo<Thumb_Jump24> {
   static constexpr HalfWords Opcode{0xf000, 0x8000};
   static constexpr HalfWords OpcodeMask{0xf800, 0x8000};
diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
index 17eba673eb4fa06..4d2dc6846297ded 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
@@ -42,6 +42,10 @@ Expected<aarch32::EdgeKind_aarch32> getJITLinkEdgeKind(uint32_t ELFType) {
     return aarch32::Arm_Call;
   case ELF::R_ARM_JUMP24:
     return aarch32::Arm_Jump24;
+  case ELF::R_ARM_MOVW_ABS_NC:
+    return aarch32::Arm_MovwAbsNC;
+  case ELF::R_ARM_MOVT_ABS:
+    return aarch32::Arm_MovtAbs;
   case ELF::R_ARM_THM_CALL:
     return aarch32::Thumb_Call;
   case ELF::R_ARM_THM_JUMP24:
@@ -68,6 +72,10 @@ Expected<uint32_t> getELFRelocationType(Edge::Kind Kind) {
     return ELF::R_ARM_CALL;
   case aarch32::Arm_Jump24:
     return ELF::R_ARM_JUMP24;
+  case aarch32::Arm_MovwAbsNC:
+    return ELF::R_ARM_MOVW_ABS_NC;
+  case aarch32::Arm_MovtAbs:
+    return ELF::R_ARM_MOVT_ABS;
   case aarch32::Thumb_Call:
     return ELF::R_ARM_THM_CALL;
   case aarch32::Thumb_Jump24:
diff --git a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
index ef278e39982c06a..915bdb4fb3cdd83 100644
--- a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
@@ -147,6 +147,48 @@ int64_t decodeRegMovtT1MovwT3(uint32_t Hi, uint32_t Lo) {
   return Rd4;
 }
 
+/// Encode 16-bit immediate value for move instruction formats MOVT A1 and
+/// MOVW A2.
+///
+///   Imm4:Imm12 -> 000000000000:Imm4:0000:Imm12
+///
+uint32_t encodeImmMovtA1MovwA2(uint16_t Value) {
+  uint32_t Imm4 = (Value >> 12) & 0x0f;
+  uint32_t Imm12 = Value & 0x0fff;
+  return (Imm4 << 16) | Imm12;
+}
+
+/// Decode 16-bit immediate value for move instruction formats MOVT A1 and
+/// MOVW A2.
+///
+///   000000000000:Imm4:0000:Imm12 -> Imm4:Imm12
+///
+uint16_t decodeImmMovtA1MovwA2(uint64_t Value) {
+  uint32_t Imm4 = (Value >> 16) & 0x0f;
+  uint32_t Imm12 = Value & 0x0fff;
+  return (Imm4 << 12) | Imm12;
+}
+
+/// Encode register ID for instruction formats MOVT A1 and
+/// MOVW A2.
+///
+///   Rd4 -> 0000000000000000:Rd4:000000000000
+///
+uint32_t encodeRegMovtA1MovwA2(int64_t Value) {
+  uint32_t Rd4 = (Value & 0x00000f) << 12;
+  return Rd4;
+}
+
+/// Decode register ID for instruction formats MOVT A1 and
+/// MOVW A2.
+///
+///   0000000000000000:Rd4:000000000000 -> Rd4
+///
+int64_t decodeRegMovtA1MovwA2(uint64_t Value) {
+  uint32_t Rd4 = (Value >> 12) & 0x00000f;
+  return Rd4;
+}
+
 /// 32-bit Thumb instructions are stored as two little-endian halfwords.
 /// An instruction at address A encodes bytes A+1, A in the first halfword (Hi),
 /// followed by bytes A+3, A+2 in the second halfword (Lo).
@@ -297,6 +339,16 @@ Expected<int64_t> readAddendArm(LinkGraph &G, Block &B, const Edge &E) {
       return makeUnexpectedOpcodeError(G, R, Kind);
     return decodeImmBA1BlA1BlxA2(R.Wd);
 
+  case Arm_MovwAbsNC:
+    if (!checkOpcode<Arm_MovwAbsNC>(R))
+      return makeUnexpectedOpcodeError(G, R, Kind);
+    return decodeImmMovtA1MovwA2(R.Wd);
+
+  case Arm_MovtAbs:
+    if (!checkOpcode<Arm_MovtAbs>(R))
+      return makeUnexpectedOpcodeError(G, R, Kind);
+    return decodeImmMovtA1MovwA2(R.Wd);
+
   default:
     return make_error<JITLinkError>(
         "In graph " + G.getName() + ", section " + B.getSection().getName() +
@@ -454,7 +506,20 @@ Error applyFixupArm(LinkGraph &G, Block &B, const Edge &E) {
 
     return Error::success();
   }
-
+  case Arm_MovwAbsNC: {
+    if (!checkOpcode<Arm_MovwAbsNC>(R))
+      return makeUnexpectedOpcodeError(G, R, Kind);
+    uint16_t Value = (TargetAddress + Addend) & 0xffff;
+    writeImmediate<Arm_MovwAbsNC>(R, encodeImmMovtA1MovwA2(Value));
+    return Error::success();
+  }
+  case Arm_MovtAbs: {
+    if (!checkOpcode<Arm_MovtAbs>(R))
+      return makeUnexpectedOpcodeError(G, R, Kind);
+    uint16_t Value = ((TargetAddress + Addend) >> 16) & 0xffff;
+    writeImmediate<Arm_MovtAbs>(R, encodeImmMovtA1MovwA2(Value));
+    return Error::success();
+  }
   default:
     return make_error<JITLinkError>(
         "In graph " + G.getName() + ", section " + B.getSection().getName() +
diff --git a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_static_arm_reloc.s b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_static_arm_reloc.s
index f91635f80f8b367..fb912f47d8b5a3b 100644
--- a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_static_arm_reloc.s
+++ b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_static_arm_reloc.s
@@ -50,6 +50,36 @@ jump24_target:
 	bx	lr
 	.size	jump24_target,	.-jump24_target
 
+
+# CHECK-TYPE: {{[0-9a-f]+}} R_ARM_MOVW_ABS_NC data_symbol
+# CHECK-INSTR: 	00000010 <movw>:
+# CHECK-INSTR: 	      10: e3000000     movw      r0, #0x0
+# jitlink-check: decode_operand(movw, 1) = (data_symbol&0x0000ffff)
+	.globl	movw
+	.type	movw,%function
+	.p2align	2
+movw:
+	movw r0, :lower16:data_symbol
+	.size	movw,	.-movw
+
+# CHECK-TYPE: {{[0-9a-f]+}} R_ARM_MOVT_ABS data_symbol
+# CHECK-INSTR: 	00000014 <movt>:
+# CHECK-INSTR: 	      14: e3400000     movt      r0, #0x0
+# jitlink-check: decode_operand(movt, 2) = (data_symbol&0xffff0000>>16)
+	.globl	movt
+	.type	movt,%function
+	.p2align	2
+movt:
+	movt r0, :upper16:data_symbol
+	.size	movt,	.-movt
+
+    .data
+	.global data_symbol
+data_symbol:
+    .long 1073741822
+
+    .text
+
 # Empty main function for jitlink to be happy
 	.globl	main
 	.type	main,%function
diff --git a/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp b/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp
index 2f9e44daea7a1db..50f6ec082d05c68 100644
--- a/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp
+++ b/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp
@@ -76,12 +76,16 @@ HalfWords encodeImmBT4BlT1BlxT2_J1J2(int64_t Value);
 uint32_t encodeImmBA1BlA1BlxA2(int64_t Value);
 HalfWords encodeImmMovtT1MovwT3(uint16_t Value);
 HalfWords encodeRegMovtT1MovwT3(int64_t Value);
+uint32_t encodeImmMovtA1MovwA2(uint16_t Value);
+uint32_t encodeRegMovtA1MovwA2(int64_t Value);
 
 int64_t decodeImmBT4BlT1BlxT2(uint32_t Hi, uint32_t Lo);
 int64_t decodeImmBT4BlT1BlxT2_J1J2(uint32_t Hi, uint32_t Lo);
 int64_t decodeImmBA1BlA1BlxA2(int64_t Value);
 uint16_t decodeImmMovtT1MovwT3(uint32_t Hi, uint32_t Lo);
 int64_t decodeRegMovtT1MovwT3(uint32_t Hi, uint32_t Lo);
+uint16_t decodeImmMovtA1MovwA2(uint64_t Value);
+int64_t decodeRegMovtA1MovwA2(uint64_t Value);
 
 } // namespace aarch32
 } // namespace jitlink
@@ -242,3 +246,41 @@ TEST(AArch32_Relocations, Thumb_MovtAbs) {
     }
   }
 }
+
+/// Write immediate value to the top halfword of the destination register
+TEST(AArch32_Relocations, Arm_MovtAbs) {
+  static_assert(isUInt<16>(65535), "Max value");
+  static_assert(!isUInt<16>(65536), "First overflow");
+
+  constexpr uint32_t ImmMask = FixupInfo<Arm_MovtAbs>::ImmMask;
+  constexpr uint32_t RegMask = FixupInfo<Arm_MovtAbs>::RegMask;
+
+  static std::array<uint8_t, 3> Registers{0, 5, 12};
+  static std::array<uint32_t, 3> MemPresets{
+      0xfeeffff7, // common
+      0x00000000, // zeros
+      0xffffffff, // ones
+  };
+
+  auto EncodeDecode = [](uint64_t In, MutableWord &Mem) {
+    Mem.patch(encodeImmMovtA1MovwA2(In), ImmMask);
+    return decodeImmMovtA1MovwA2(Mem.Wd);
+  };
+
+  for (MutableWord Mem : MemPresets) {
+    for (uint8_t Reg : Registers) {
+      MutableWord UnaffectedBits(Mem.Wd & ~(ImmMask | RegMask));
+
+      Mem.patch(encodeRegMovtA1MovwA2(Reg), RegMask);
+      EXPECT_EQ(EncodeDecode(0x76bb, Mem), 0x76bb);   // Common value
+      EXPECT_EQ(EncodeDecode(0, Mem), 0);             // Minimum value
+      EXPECT_EQ(EncodeDecode(0xffff, Mem), 0xffff);   // Maximum value
+      EXPECT_NE(EncodeDecode(0x10000, Mem), 0x10000); // First overflow
+
+      // Destination register as well as unaffacted bits should be intact
+      EXPECT_EQ(decodeRegMovtA1MovwA2(Mem.Wd), Reg);
+      EXPECT_TRUE(UnaffectedBits.Wd == (Mem.Wd & ~(ImmMask | RegMask)))
+          << "Diff outside immediate/register field";
+    }
+  }
+}

>From da452f97d92ea06c3d2941b64a4477ea50c69b58 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Eymen=20=C3=9Cnay?= <eymenunay at outlook.com>
Date: Wed, 13 Sep 2023 20:51:25 +0300
Subject: [PATCH 2/2] fixup! [JITlink][AArch32] Implement ELF::R_ARM_MOVT_ABS
 and R_ARM_MOVW_ABS_NC

---
 .../JITLink/AArch32/ELF_static_arm_reloc.s                | 8 +++++---
 llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp   | 8 ++++----
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_static_arm_reloc.s b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_static_arm_reloc.s
index fb912f47d8b5a3b..6639e3258a72a33 100644
--- a/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_static_arm_reloc.s
+++ b/llvm/test/ExecutionEngine/JITLink/AArch32/ELF_static_arm_reloc.s
@@ -65,6 +65,8 @@ movw:
 # CHECK-TYPE: {{[0-9a-f]+}} R_ARM_MOVT_ABS data_symbol
 # CHECK-INSTR: 	00000014 <movt>:
 # CHECK-INSTR: 	      14: e3400000     movt      r0, #0x0
+# We decode the operand with index 2, because movt generates two leading implicit 
+# predicate operands that we have to skip in order to decode the data_symbol operand 
 # jitlink-check: decode_operand(movt, 2) = (data_symbol&0xffff0000>>16)
 	.globl	movt
 	.type	movt,%function
@@ -73,12 +75,12 @@ movt:
 	movt r0, :upper16:data_symbol
 	.size	movt,	.-movt
 
-    .data
+	.data
 	.global data_symbol
 data_symbol:
-    .long 1073741822
+	.long 1073741822
 
-    .text
+	.text
 
 # Empty main function for jitlink to be happy
 	.globl	main
diff --git a/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp b/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp
index 50f6ec082d05c68..3f581445a2d6295 100644
--- a/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp
+++ b/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp
@@ -187,7 +187,7 @@ TEST(AArch32_Relocations, Arm_Call_Bare) {
       0xffffffff, // ones
   };
 
-  auto EncodeDecode = [](int64_t In, MutableWord &Mem) {
+  auto EncodeDecode = [=](int64_t In, MutableWord &Mem) {
     Mem.patch(encodeImmBA1BlA1BlxA2(In), ImmMask);
     return decodeImmBA1BlA1BlxA2(Mem.Wd);
   };
@@ -238,7 +238,7 @@ TEST(AArch32_Relocations, Thumb_MovtAbs) {
       EXPECT_EQ(EncodeDecode(0xffff, Mem), 0xffff);   // Maximum value
       EXPECT_NE(EncodeDecode(0x10000, Mem), 0x10000); // First overflow
 
-      // Destination register as well as unaffacted bits should be intact
+      // Destination register as well as unaffected bits should be intact
       EXPECT_EQ(decodeRegMovtT1MovwT3(Mem.Hi, Mem.Lo), Reg);
       EXPECT_TRUE(UnaffectedBits.Hi == (Mem.Hi & ~(ImmMask.Hi | RegMask.Hi)) &&
                   UnaffectedBits.Lo == (Mem.Lo & ~(ImmMask.Lo | RegMask.Lo)))
@@ -262,7 +262,7 @@ TEST(AArch32_Relocations, Arm_MovtAbs) {
       0xffffffff, // ones
   };
 
-  auto EncodeDecode = [](uint64_t In, MutableWord &Mem) {
+  auto EncodeDecode = [=](uint64_t In, MutableWord &Mem) {
     Mem.patch(encodeImmMovtA1MovwA2(In), ImmMask);
     return decodeImmMovtA1MovwA2(Mem.Wd);
   };
@@ -277,7 +277,7 @@ TEST(AArch32_Relocations, Arm_MovtAbs) {
       EXPECT_EQ(EncodeDecode(0xffff, Mem), 0xffff);   // Maximum value
       EXPECT_NE(EncodeDecode(0x10000, Mem), 0x10000); // First overflow
 
-      // Destination register as well as unaffacted bits should be intact
+      // Destination register as well as unaffected bits should be intact
       EXPECT_EQ(decodeRegMovtA1MovwA2(Mem.Wd), Reg);
       EXPECT_TRUE(UnaffectedBits.Wd == (Mem.Wd & ~(ImmMask | RegMask)))
           << "Diff outside immediate/register field";



More information about the llvm-commits mailing list