[llvm] e575b7c - [JITlink][AArch32] Implement ELF::R_ARM_MOVT_ABS and R_ARM_MOVW_ABS_NC (#66219)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 14 17:50:57 PDT 2023
Author: Eymen Ünay
Date: 2023-09-14T17:50:53-07:00
New Revision: e575b7cb7a64297583d6382c16ce264d9fe45d08
URL: https://github.com/llvm/llvm-project/commit/e575b7cb7a64297583d6382c16ce264d9fe45d08
DIFF: https://github.com/llvm/llvm-project/commit/e575b7cb7a64297583d6382c16ce264d9fe45d08.diff
LOG: [JITlink][AArch32] Implement ELF::R_ARM_MOVT_ABS and R_ARM_MOVW_ABS_NC (#66219)
Add support for static Arm relocations of R_ARM_MOVT_ABS and R_ARM_MOVW_ABS_NC
which are emitted by movt and movw instructions. The implementation contains
relocation fixup and its testing as well as its encode/decode functions for
reading and writing immediate values together with its unittests.
Added:
Modified:
llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
llvm/test/ExecutionEngine/JITLink/AArch32/ELF_static_arm_reloc.s
llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
index 05391baecb9a59b..3f36b53d6684a79 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
@@ -59,7 +59,13 @@ enum EdgeKind_aarch32 : Edge::Kind {
/// interworking stub.
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)
@@ -174,6 +180,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 0e5aa77b8f4388e..37ced88169b0ac4 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() +
@@ -450,7 +502,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() +
@@ -590,6 +655,8 @@ const char *getEdgeKindName(Edge::Kind K) {
KIND_NAME_CASE(Data_Pointer32)
KIND_NAME_CASE(Arm_Call)
KIND_NAME_CASE(Arm_Jump24)
+ KIND_NAME_CASE(Arm_MovwAbsNC)
+ KIND_NAME_CASE(Arm_MovtAbs)
KIND_NAME_CASE(Thumb_Call)
KIND_NAME_CASE(Thumb_Jump24)
KIND_NAME_CASE(Thumb_MovwAbsNC)
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..cc65193af736f3b 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,38 @@ 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
+# We decode the operand with index 2, because movt generates one leading implicit
+# predicate operand 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
+ .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 3c2bb189ff1ba6b..3f581445a2d6295 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 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