Skip to content

Commit e575b7c

Browse files
authored
[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.
1 parent d2ab97b commit e575b7c

File tree

5 files changed

+168
-2
lines changed

5 files changed

+168
-2
lines changed

llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,13 @@ enum EdgeKind_aarch32 : Edge::Kind {
5959
/// interworking stub.
6060
Arm_Jump24,
6161

62-
LastArmRelocation = Arm_Jump24,
62+
/// Write immediate value to the lower halfword of the destination register
63+
Arm_MovwAbsNC,
64+
65+
/// Write immediate value to the top halfword of the destination register
66+
Arm_MovtAbs,
67+
68+
LastArmRelocation = Arm_MovtAbs,
6369

6470
///
6571
/// Relocations of class Thumb16 and Thumb32 (covers Thumb instruction subset)
@@ -174,6 +180,17 @@ template <> struct FixupInfo<Arm_Call> : public FixupInfo<Arm_Jump24> {
174180
static constexpr uint32_t BitBlx = 0x10000000;
175181
};
176182

183+
template <> struct FixupInfo<Arm_MovtAbs> {
184+
static constexpr uint32_t Opcode = 0x03400000;
185+
static constexpr uint32_t OpcodeMask = 0x0ff00000;
186+
static constexpr uint32_t ImmMask = 0x000f0fff;
187+
static constexpr uint32_t RegMask = 0x0000f000;
188+
};
189+
190+
template <> struct FixupInfo<Arm_MovwAbsNC> : public FixupInfo<Arm_MovtAbs> {
191+
static constexpr uint32_t Opcode = 0x03000000;
192+
};
193+
177194
template <> struct FixupInfo<Thumb_Jump24> {
178195
static constexpr HalfWords Opcode{0xf000, 0x8000};
179196
static constexpr HalfWords OpcodeMask{0xf800, 0x8000};

llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ Expected<aarch32::EdgeKind_aarch32> getJITLinkEdgeKind(uint32_t ELFType) {
4242
return aarch32::Arm_Call;
4343
case ELF::R_ARM_JUMP24:
4444
return aarch32::Arm_Jump24;
45+
case ELF::R_ARM_MOVW_ABS_NC:
46+
return aarch32::Arm_MovwAbsNC;
47+
case ELF::R_ARM_MOVT_ABS:
48+
return aarch32::Arm_MovtAbs;
4549
case ELF::R_ARM_THM_CALL:
4650
return aarch32::Thumb_Call;
4751
case ELF::R_ARM_THM_JUMP24:
@@ -68,6 +72,10 @@ Expected<uint32_t> getELFRelocationType(Edge::Kind Kind) {
6872
return ELF::R_ARM_CALL;
6973
case aarch32::Arm_Jump24:
7074
return ELF::R_ARM_JUMP24;
75+
case aarch32::Arm_MovwAbsNC:
76+
return ELF::R_ARM_MOVW_ABS_NC;
77+
case aarch32::Arm_MovtAbs:
78+
return ELF::R_ARM_MOVT_ABS;
7179
case aarch32::Thumb_Call:
7280
return ELF::R_ARM_THM_CALL;
7381
case aarch32::Thumb_Jump24:

llvm/lib/ExecutionEngine/JITLink/aarch32.cpp

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,48 @@ int64_t decodeRegMovtT1MovwT3(uint32_t Hi, uint32_t Lo) {
147147
return Rd4;
148148
}
149149

150+
/// Encode 16-bit immediate value for move instruction formats MOVT A1 and
151+
/// MOVW A2.
152+
///
153+
/// Imm4:Imm12 -> 000000000000:Imm4:0000:Imm12
154+
///
155+
uint32_t encodeImmMovtA1MovwA2(uint16_t Value) {
156+
uint32_t Imm4 = (Value >> 12) & 0x0f;
157+
uint32_t Imm12 = Value & 0x0fff;
158+
return (Imm4 << 16) | Imm12;
159+
}
160+
161+
/// Decode 16-bit immediate value for move instruction formats MOVT A1 and
162+
/// MOVW A2.
163+
///
164+
/// 000000000000:Imm4:0000:Imm12 -> Imm4:Imm12
165+
///
166+
uint16_t decodeImmMovtA1MovwA2(uint64_t Value) {
167+
uint32_t Imm4 = (Value >> 16) & 0x0f;
168+
uint32_t Imm12 = Value & 0x0fff;
169+
return (Imm4 << 12) | Imm12;
170+
}
171+
172+
/// Encode register ID for instruction formats MOVT A1 and
173+
/// MOVW A2.
174+
///
175+
/// Rd4 -> 0000000000000000:Rd4:000000000000
176+
///
177+
uint32_t encodeRegMovtA1MovwA2(int64_t Value) {
178+
uint32_t Rd4 = (Value & 0x00000f) << 12;
179+
return Rd4;
180+
}
181+
182+
/// Decode register ID for instruction formats MOVT A1 and
183+
/// MOVW A2.
184+
///
185+
/// 0000000000000000:Rd4:000000000000 -> Rd4
186+
///
187+
int64_t decodeRegMovtA1MovwA2(uint64_t Value) {
188+
uint32_t Rd4 = (Value >> 12) & 0x00000f;
189+
return Rd4;
190+
}
191+
150192
/// 32-bit Thumb instructions are stored as two little-endian halfwords.
151193
/// An instruction at address A encodes bytes A+1, A in the first halfword (Hi),
152194
/// 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) {
297339
return makeUnexpectedOpcodeError(G, R, Kind);
298340
return decodeImmBA1BlA1BlxA2(R.Wd);
299341

342+
case Arm_MovwAbsNC:
343+
if (!checkOpcode<Arm_MovwAbsNC>(R))
344+
return makeUnexpectedOpcodeError(G, R, Kind);
345+
return decodeImmMovtA1MovwA2(R.Wd);
346+
347+
case Arm_MovtAbs:
348+
if (!checkOpcode<Arm_MovtAbs>(R))
349+
return makeUnexpectedOpcodeError(G, R, Kind);
350+
return decodeImmMovtA1MovwA2(R.Wd);
351+
300352
default:
301353
return make_error<JITLinkError>(
302354
"In graph " + G.getName() + ", section " + B.getSection().getName() +
@@ -450,7 +502,20 @@ Error applyFixupArm(LinkGraph &G, Block &B, const Edge &E) {
450502

451503
return Error::success();
452504
}
453-
505+
case Arm_MovwAbsNC: {
506+
if (!checkOpcode<Arm_MovwAbsNC>(R))
507+
return makeUnexpectedOpcodeError(G, R, Kind);
508+
uint16_t Value = (TargetAddress + Addend) & 0xffff;
509+
writeImmediate<Arm_MovwAbsNC>(R, encodeImmMovtA1MovwA2(Value));
510+
return Error::success();
511+
}
512+
case Arm_MovtAbs: {
513+
if (!checkOpcode<Arm_MovtAbs>(R))
514+
return makeUnexpectedOpcodeError(G, R, Kind);
515+
uint16_t Value = ((TargetAddress + Addend) >> 16) & 0xffff;
516+
writeImmediate<Arm_MovtAbs>(R, encodeImmMovtA1MovwA2(Value));
517+
return Error::success();
518+
}
454519
default:
455520
return make_error<JITLinkError>(
456521
"In graph " + G.getName() + ", section " + B.getSection().getName() +
@@ -590,6 +655,8 @@ const char *getEdgeKindName(Edge::Kind K) {
590655
KIND_NAME_CASE(Data_Pointer32)
591656
KIND_NAME_CASE(Arm_Call)
592657
KIND_NAME_CASE(Arm_Jump24)
658+
KIND_NAME_CASE(Arm_MovwAbsNC)
659+
KIND_NAME_CASE(Arm_MovtAbs)
593660
KIND_NAME_CASE(Thumb_Call)
594661
KIND_NAME_CASE(Thumb_Jump24)
595662
KIND_NAME_CASE(Thumb_MovwAbsNC)

llvm/test/ExecutionEngine/JITLink/AArch32/ELF_static_arm_reloc.s

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,38 @@ jump24_target:
5050
bx lr
5151
.size jump24_target, .-jump24_target
5252

53+
54+
# CHECK-TYPE: {{[0-9a-f]+}} R_ARM_MOVW_ABS_NC data_symbol
55+
# CHECK-INSTR: 00000010 <movw>:
56+
# CHECK-INSTR: 10: e3000000 movw r0, #0x0
57+
# jitlink-check: decode_operand(movw, 1) = (data_symbol&0x0000ffff)
58+
.globl movw
59+
.type movw,%function
60+
.p2align 2
61+
movw:
62+
movw r0, :lower16:data_symbol
63+
.size movw, .-movw
64+
65+
# CHECK-TYPE: {{[0-9a-f]+}} R_ARM_MOVT_ABS data_symbol
66+
# CHECK-INSTR: 00000014 <movt>:
67+
# CHECK-INSTR: 14: e3400000 movt r0, #0x0
68+
# We decode the operand with index 2, because movt generates one leading implicit
69+
# predicate operand that we have to skip in order to decode the data_symbol operand
70+
# jitlink-check: decode_operand(movt, 2) = (data_symbol&0xffff0000>>16)
71+
.globl movt
72+
.type movt,%function
73+
.p2align 2
74+
movt:
75+
movt r0, :upper16:data_symbol
76+
.size movt, .-movt
77+
78+
.data
79+
.global data_symbol
80+
data_symbol:
81+
.long 1073741822
82+
83+
.text
84+
5385
# Empty main function for jitlink to be happy
5486
.globl main
5587
.type main,%function

llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,16 @@ HalfWords encodeImmBT4BlT1BlxT2_J1J2(int64_t Value);
7676
uint32_t encodeImmBA1BlA1BlxA2(int64_t Value);
7777
HalfWords encodeImmMovtT1MovwT3(uint16_t Value);
7878
HalfWords encodeRegMovtT1MovwT3(int64_t Value);
79+
uint32_t encodeImmMovtA1MovwA2(uint16_t Value);
80+
uint32_t encodeRegMovtA1MovwA2(int64_t Value);
7981

8082
int64_t decodeImmBT4BlT1BlxT2(uint32_t Hi, uint32_t Lo);
8183
int64_t decodeImmBT4BlT1BlxT2_J1J2(uint32_t Hi, uint32_t Lo);
8284
int64_t decodeImmBA1BlA1BlxA2(int64_t Value);
8385
uint16_t decodeImmMovtT1MovwT3(uint32_t Hi, uint32_t Lo);
8486
int64_t decodeRegMovtT1MovwT3(uint32_t Hi, uint32_t Lo);
87+
uint16_t decodeImmMovtA1MovwA2(uint64_t Value);
88+
int64_t decodeRegMovtA1MovwA2(uint64_t Value);
8589

8690
} // namespace aarch32
8791
} // namespace jitlink
@@ -242,3 +246,41 @@ TEST(AArch32_Relocations, Thumb_MovtAbs) {
242246
}
243247
}
244248
}
249+
250+
/// Write immediate value to the top halfword of the destination register
251+
TEST(AArch32_Relocations, Arm_MovtAbs) {
252+
static_assert(isUInt<16>(65535), "Max value");
253+
static_assert(!isUInt<16>(65536), "First overflow");
254+
255+
constexpr uint32_t ImmMask = FixupInfo<Arm_MovtAbs>::ImmMask;
256+
constexpr uint32_t RegMask = FixupInfo<Arm_MovtAbs>::RegMask;
257+
258+
static std::array<uint8_t, 3> Registers{0, 5, 12};
259+
static std::array<uint32_t, 3> MemPresets{
260+
0xfeeffff7, // common
261+
0x00000000, // zeros
262+
0xffffffff, // ones
263+
};
264+
265+
auto EncodeDecode = [=](uint64_t In, MutableWord &Mem) {
266+
Mem.patch(encodeImmMovtA1MovwA2(In), ImmMask);
267+
return decodeImmMovtA1MovwA2(Mem.Wd);
268+
};
269+
270+
for (MutableWord Mem : MemPresets) {
271+
for (uint8_t Reg : Registers) {
272+
MutableWord UnaffectedBits(Mem.Wd & ~(ImmMask | RegMask));
273+
274+
Mem.patch(encodeRegMovtA1MovwA2(Reg), RegMask);
275+
EXPECT_EQ(EncodeDecode(0x76bb, Mem), 0x76bb); // Common value
276+
EXPECT_EQ(EncodeDecode(0, Mem), 0); // Minimum value
277+
EXPECT_EQ(EncodeDecode(0xffff, Mem), 0xffff); // Maximum value
278+
EXPECT_NE(EncodeDecode(0x10000, Mem), 0x10000); // First overflow
279+
280+
// Destination register as well as unaffected bits should be intact
281+
EXPECT_EQ(decodeRegMovtA1MovwA2(Mem.Wd), Reg);
282+
EXPECT_TRUE(UnaffectedBits.Wd == (Mem.Wd & ~(ImmMask | RegMask)))
283+
<< "Diff outside immediate/register field";
284+
}
285+
}
286+
}

0 commit comments

Comments
 (0)