xref: /llvm-project/llvm/unittests/ExecutionEngine/JITLink/AArch32Tests.cpp (revision 565470ed27131b2550e71cf334bf3f33f9d82c62)
1 //===------- AArch32Tests.cpp - Unit tests for the AArch32 backend --------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include <llvm/BinaryFormat/ELF.h>
10 #include <llvm/ExecutionEngine/JITLink/aarch32.h>
11 
12 #include "gtest/gtest.h"
13 
14 using namespace llvm;
15 using namespace llvm::jitlink;
16 using namespace llvm::jitlink::aarch32;
17 using namespace llvm::support;
18 using namespace llvm::support::endian;
19 
20 struct MutableHalfWords {
MutableHalfWordsMutableHalfWords21   MutableHalfWords(HalfWords Preset) : Hi(Preset.Hi), Lo(Preset.Lo) {}
22 
patchMutableHalfWords23   void patch(HalfWords Value, HalfWords Mask) {
24     Hi = (Hi & ~Mask.Hi) | Value.Hi;
25     Lo = (Lo & ~Mask.Lo) | Value.Lo;
26   }
27 
28   uint16_t Hi; // First halfword
29   uint16_t Lo; // Second halfword
30 };
31 
32 struct MutableWord {
MutableWordMutableWord33   MutableWord(uint32_t Preset) : Wd(Preset) {}
34 
patchMutableWord35   void patch(uint32_t Value, uint32_t Mask) { Wd = (Wd & ~Mask) | Value; }
36 
37   uint32_t Wd;
38 };
39 namespace llvm {
40 namespace jitlink {
41 
42 Expected<aarch32::EdgeKind_aarch32>
43 getJITLinkEdgeKind(uint32_t ELFType, const aarch32::ArmConfig &Cfg);
44 Expected<uint32_t> getELFRelocationType(Edge::Kind Kind);
45 
46 } // namespace jitlink
47 } // namespace llvm
48 
TEST(AArch32_ELF,EdgeKinds)49 TEST(AArch32_ELF, EdgeKinds) {
50   // Fails: Invalid ELF type -> JITLink kind
51   aarch32::ArmConfig Cfg;
52   Expected<uint32_t> ErrKind = getJITLinkEdgeKind(ELF::R_ARM_ME_TOO, Cfg);
53   EXPECT_TRUE(errorToBool(ErrKind.takeError()));
54 
55   // Fails: Invalid JITLink kind -> ELF type
56   Expected<uint32_t> ErrType = getELFRelocationType(Edge::Invalid);
57   EXPECT_TRUE(errorToBool(ErrType.takeError()));
58 
59   for (Edge::Kind K = FirstDataRelocation; K < LastThumbRelocation; K += 1) {
60     Expected<uint32_t> ELFType = getELFRelocationType(K);
61     EXPECT_FALSE(errorToBool(ELFType.takeError()))
62         << "Failed to translate JITLink kind -> ELF type";
63 
64     Expected<Edge::Kind> JITLinkKind = getJITLinkEdgeKind(*ELFType, Cfg);
65     EXPECT_FALSE(errorToBool(JITLinkKind.takeError()))
66         << "Failed to translate ELF type -> JITLink kind";
67 
68     EXPECT_EQ(*JITLinkKind, K) << "Round-trip value inconsistent?";
69   }
70 }
71 
TEST(AArch32_ELF,DynFixupInfos)72 TEST(AArch32_ELF, DynFixupInfos) {
73   // We can do an opcode check for all Arm edges
74   for (Edge::Kind K = FirstArmRelocation; K < LastArmRelocation; K += 1) {
75     const auto *Info = FixupInfoBase::getDynFixupInfo(K);
76     EXPECT_NE(Info, nullptr);
77     const auto *InfoArm = static_cast<const FixupInfoArm *>(Info);
78     EXPECT_NE(InfoArm->checkOpcode, nullptr);
79     EXPECT_FALSE(InfoArm->checkOpcode(0x00000000));
80   }
81   // We can do an opcode check for all Thumb edges
82   for (Edge::Kind K = FirstThumbRelocation; K < LastThumbRelocation; K += 1) {
83     const auto *Info = FixupInfoBase::getDynFixupInfo(K);
84     EXPECT_NE(Info, nullptr);
85     const auto *InfoThumb = static_cast<const FixupInfoThumb *>(Info);
86     EXPECT_NE(InfoThumb->checkOpcode, nullptr);
87     EXPECT_FALSE(InfoThumb->checkOpcode(0x0000, 0x0000));
88   }
89   // We cannot do it for Data and generic edges
90   EXPECT_EQ(FixupInfoBase::getDynFixupInfo(FirstDataRelocation), nullptr);
91   EXPECT_EQ(FixupInfoBase::getDynFixupInfo(Edge::GenericEdgeKind::Invalid),
92             nullptr);
93 }
94 
95 namespace llvm {
96 namespace jitlink {
97 namespace aarch32 {
98 
99 HalfWords encodeImmBT4BlT1BlxT2(int64_t Value);
100 HalfWords encodeImmBT4BlT1BlxT2_J1J2(int64_t Value);
101 uint32_t encodeImmBA1BlA1BlxA2(int64_t Value);
102 HalfWords encodeImmMovtT1MovwT3(uint16_t Value);
103 HalfWords encodeRegMovtT1MovwT3(int64_t Value);
104 uint32_t encodeImmMovtA1MovwA2(uint16_t Value);
105 uint32_t encodeRegMovtA1MovwA2(int64_t Value);
106 
107 int64_t decodeImmBT4BlT1BlxT2(uint32_t Hi, uint32_t Lo);
108 int64_t decodeImmBT4BlT1BlxT2_J1J2(uint32_t Hi, uint32_t Lo);
109 int64_t decodeImmBA1BlA1BlxA2(int64_t Value);
110 uint16_t decodeImmMovtT1MovwT3(uint32_t Hi, uint32_t Lo);
111 int64_t decodeRegMovtT1MovwT3(uint32_t Hi, uint32_t Lo);
112 uint16_t decodeImmMovtA1MovwA2(uint64_t Value);
113 int64_t decodeRegMovtA1MovwA2(uint64_t Value);
114 
115 } // namespace aarch32
116 } // namespace jitlink
117 } // namespace llvm
118 
119 // Big-endian for v7 and v8 (and v6 unless in legacy backwards compatible mode
120 // be32) have little-endian instructions and big-endian data. In ELF relocatable
121 // objects big-endian instructions may still be encountered. A be8 supporting
122 // linker is expected to endian-reverse instructions for the executable.
123 template <endianness Endian>
makeHalfWords(std::array<uint8_t,4> Mem)124 static HalfWords makeHalfWords(std::array<uint8_t, 4> Mem) {
125   return HalfWords{read16<Endian>(Mem.data()), read16<Endian>(Mem.data() + 2)};
126 }
127 
128 /// 25-bit branch with link (with J1J2 range extension)
TEST(AArch32_Relocations,Thumb_Call_J1J2)129 TEST(AArch32_Relocations, Thumb_Call_J1J2) {
130   static_assert(isInt<25>(16777215), "Max value");
131   static_assert(isInt<25>(-16777215), "Min value");
132   static_assert(!isInt<25>(16777217), "First overflow");
133   static_assert(!isInt<25>(-16777217), "First underflow");
134 
135   constexpr HalfWords ImmMask = FixupInfo<Thumb_Call>::ImmMask;
136 
137   static std::array<HalfWords, 3> MemPresets{
138       makeHalfWords<endianness::little>({0xff, 0xf7, 0xfe, 0xef}), // common
139       makeHalfWords<endianness::little>({0x00, 0x00, 0x00, 0x00}), // zeros
140       makeHalfWords<endianness::little>({0xff, 0xff, 0xff, 0xff}), // ones
141   };
142 
143   auto EncodeDecode = [ImmMask](int64_t In, MutableHalfWords &Mem) {
144     Mem.patch(encodeImmBT4BlT1BlxT2_J1J2(In), ImmMask);
145     return decodeImmBT4BlT1BlxT2_J1J2(Mem.Hi, Mem.Lo);
146   };
147 
148   for (MutableHalfWords Mem : MemPresets) {
149     HalfWords UnaffectedBits(Mem.Hi & ~ImmMask.Hi, Mem.Lo & ~ImmMask.Lo);
150 
151     EXPECT_EQ(EncodeDecode(1, Mem), 0);                 // Zero value
152     EXPECT_EQ(EncodeDecode(0x41, Mem), 0x40);           // Common value
153     EXPECT_EQ(EncodeDecode(16777215, Mem), 16777214);   // Maximum value
154     EXPECT_EQ(EncodeDecode(-16777215, Mem), -16777216); // Minimum value
155     EXPECT_NE(EncodeDecode(16777217, Mem), 16777217);   // First overflow
156     EXPECT_NE(EncodeDecode(-16777217, Mem), -16777217); // First underflow
157 
158     EXPECT_TRUE(UnaffectedBits.Hi == (Mem.Hi & ~ImmMask.Hi) &&
159                 UnaffectedBits.Lo == (Mem.Lo & ~ImmMask.Lo))
160         << "Diff outside immediate field";
161   }
162 }
163 
164 /// 22-bit branch with link (without J1J2 range extension)
TEST(AArch32_Relocations,Thumb_Call_Bare)165 TEST(AArch32_Relocations, Thumb_Call_Bare) {
166   static_assert(isInt<22>(2097151), "Max value");
167   static_assert(isInt<22>(-2097151), "Min value");
168   static_assert(!isInt<22>(2097153), "First overflow");
169   static_assert(!isInt<22>(-2097153), "First underflow");
170 
171   constexpr HalfWords ImmMask = FixupInfo<Thumb_Call>::ImmMask;
172 
173   static std::array<HalfWords, 3> MemPresets{
174       makeHalfWords<endianness::little>({0xff, 0xf7, 0xfe, 0xef}), // common
175       makeHalfWords<endianness::little>({0x00, 0x00, 0x00, 0x00}), // zeros
176       makeHalfWords<endianness::little>({0xff, 0xff, 0xff, 0xff}), // ones
177   };
178 
179   auto EncodeDecode = [ImmMask](int64_t In, MutableHalfWords &Mem) {
180     Mem.patch(encodeImmBT4BlT1BlxT2_J1J2(In), ImmMask);
181     return decodeImmBT4BlT1BlxT2_J1J2(Mem.Hi, Mem.Lo);
182   };
183 
184   for (MutableHalfWords Mem : MemPresets) {
185     HalfWords UnaffectedBits(Mem.Hi & ~ImmMask.Hi, Mem.Lo & ~ImmMask.Lo);
186 
187     EXPECT_EQ(EncodeDecode(1, Mem), 0);               // Zero value
188     EXPECT_EQ(EncodeDecode(0x41, Mem), 0x40);         // Common value
189     EXPECT_EQ(EncodeDecode(2097151, Mem), 2097150);   // Maximum value
190     EXPECT_EQ(EncodeDecode(-2097151, Mem), -2097152); // Minimum value
191     EXPECT_NE(EncodeDecode(2097153, Mem), 2097153);   // First overflow
192     EXPECT_NE(EncodeDecode(-2097153, Mem), -2097153); // First underflow
193 
194     EXPECT_TRUE(UnaffectedBits.Hi == (Mem.Hi & ~ImmMask.Hi) &&
195                 UnaffectedBits.Lo == (Mem.Lo & ~ImmMask.Lo))
196         << "Diff outside immediate field";
197   }
198 }
199 
200 /// 26-bit branch with link
TEST(AArch32_Relocations,Arm_Call_Bare)201 TEST(AArch32_Relocations, Arm_Call_Bare) {
202   static_assert(isInt<26>(33554430), "Max value");
203   static_assert(isInt<26>(-33554432), "Min value");
204   static_assert(!isInt<26>(33554432), "First overflow");
205   static_assert(!isInt<26>(-33554434), "First underflow");
206 
207   constexpr uint32_t ImmMask = FixupInfo<Arm_Call>::ImmMask;
208 
209   static std::array<uint32_t, 3> MemPresets{
210       0xfeeffff7, // common
211       0x00000000, // zeros
212       0xffffffff, // ones
213   };
214 
215   auto EncodeDecode = [=](int64_t In, MutableWord &Mem) {
216     Mem.patch(encodeImmBA1BlA1BlxA2(In), ImmMask);
217     return decodeImmBA1BlA1BlxA2(Mem.Wd);
218   };
219 
220   for (MutableWord Mem : MemPresets) {
221     MutableWord UnaffectedBits(Mem.Wd & ~ImmMask);
222 
223     EXPECT_EQ(EncodeDecode(0, Mem), 0);                 // Zero value
224     EXPECT_EQ(EncodeDecode(0x40, Mem), 0x40);           // Common value
225     EXPECT_EQ(EncodeDecode(33554428, Mem), 33554428);   // Maximum value
226     EXPECT_EQ(EncodeDecode(-33554432, Mem), -33554432); // Minimum value
227     EXPECT_NE(EncodeDecode(33554434, Mem), 33554434);   // First overflow
228     EXPECT_NE(EncodeDecode(-33554434, Mem), -33554434); // First underflow
229 
230     EXPECT_TRUE(UnaffectedBits.Wd == (Mem.Wd & ~ImmMask))
231         << "Diff outside immediate field";
232   }
233 }
234 
235 /// Write immediate value to the top halfword of the destination register
TEST(AArch32_Relocations,Thumb_MovtAbs)236 TEST(AArch32_Relocations, Thumb_MovtAbs) {
237   static_assert(isUInt<16>(65535), "Max value");
238   static_assert(!isUInt<16>(65536), "First overflow");
239 
240   constexpr HalfWords ImmMask = FixupInfo<Thumb_MovtAbs>::ImmMask;
241   constexpr HalfWords RegMask = FixupInfo<Thumb_MovtAbs>::RegMask;
242 
243   static std::array<uint8_t, 3> Registers{0, 5, 12};
244   static std::array<HalfWords, 3> MemPresets{
245       makeHalfWords<endianness::little>({0xff, 0xf7, 0xfe, 0xef}), // common
246       makeHalfWords<endianness::little>({0x00, 0x00, 0x00, 0x00}), // zeros
247       makeHalfWords<endianness::little>({0xff, 0xff, 0xff, 0xff}), // ones
248   };
249 
250   auto EncodeDecode = [ImmMask](uint32_t In, MutableHalfWords &Mem) {
251     Mem.patch(encodeImmMovtT1MovwT3(In), ImmMask);
252     return decodeImmMovtT1MovwT3(Mem.Hi, Mem.Lo);
253   };
254 
255   for (MutableHalfWords Mem : MemPresets) {
256     for (uint8_t Reg : Registers) {
257       HalfWords UnaffectedBits(Mem.Hi & ~(ImmMask.Hi | RegMask.Hi),
258                                Mem.Lo & ~(ImmMask.Lo | RegMask.Lo));
259 
260       Mem.patch(encodeRegMovtT1MovwT3(Reg), RegMask);
261       EXPECT_EQ(EncodeDecode(0x76bb, Mem), 0x76bb);   // Common value
262       EXPECT_EQ(EncodeDecode(0, Mem), 0);             // Minimum value
263       EXPECT_EQ(EncodeDecode(0xffff, Mem), 0xffff);   // Maximum value
264       EXPECT_NE(EncodeDecode(0x10000, Mem), 0x10000); // First overflow
265 
266       // Destination register as well as unaffected bits should be intact
267       EXPECT_EQ(decodeRegMovtT1MovwT3(Mem.Hi, Mem.Lo), Reg);
268       EXPECT_TRUE(UnaffectedBits.Hi == (Mem.Hi & ~(ImmMask.Hi | RegMask.Hi)) &&
269                   UnaffectedBits.Lo == (Mem.Lo & ~(ImmMask.Lo | RegMask.Lo)))
270           << "Diff outside immediate/register field";
271     }
272   }
273 }
274 
275 /// Write immediate value to the top halfword of the destination register
TEST(AArch32_Relocations,Arm_MovtAbs)276 TEST(AArch32_Relocations, Arm_MovtAbs) {
277   static_assert(isUInt<16>(65535), "Max value");
278   static_assert(!isUInt<16>(65536), "First overflow");
279 
280   constexpr uint32_t ImmMask = FixupInfo<Arm_MovtAbs>::ImmMask;
281   constexpr uint32_t RegMask = FixupInfo<Arm_MovtAbs>::RegMask;
282 
283   static std::array<uint8_t, 3> Registers{0, 5, 12};
284   static std::array<uint32_t, 3> MemPresets{
285       0xfeeffff7, // common
286       0x00000000, // zeros
287       0xffffffff, // ones
288   };
289 
290   auto EncodeDecode = [=](uint64_t In, MutableWord &Mem) {
291     Mem.patch(encodeImmMovtA1MovwA2(In), ImmMask);
292     return decodeImmMovtA1MovwA2(Mem.Wd);
293   };
294 
295   for (MutableWord Mem : MemPresets) {
296     for (uint8_t Reg : Registers) {
297       MutableWord UnaffectedBits(Mem.Wd & ~(ImmMask | RegMask));
298 
299       Mem.patch(encodeRegMovtA1MovwA2(Reg), RegMask);
300       EXPECT_EQ(EncodeDecode(0x76bb, Mem), 0x76bb);   // Common value
301       EXPECT_EQ(EncodeDecode(0, Mem), 0);             // Minimum value
302       EXPECT_EQ(EncodeDecode(0xffff, Mem), 0xffff);   // Maximum value
303       EXPECT_NE(EncodeDecode(0x10000, Mem), 0x10000); // First overflow
304 
305       // Destination register as well as unaffected bits should be intact
306       EXPECT_EQ(decodeRegMovtA1MovwA2(Mem.Wd), Reg);
307       EXPECT_TRUE(UnaffectedBits.Wd == (Mem.Wd & ~(ImmMask | RegMask)))
308           << "Diff outside immediate/register field";
309     }
310   }
311 }
312