1 //===------- AArch32Tests.cpp - Unit tests for the AArch32 backend --------===//
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
7 //===----------------------------------------------------------------------===//
9 #include <llvm/BinaryFormat/ELF.h>
10 #include <llvm/ExecutionEngine/JITLink/aarch32.h>
12 #include "gtest/gtest.h"
15 using namespace llvm::jitlink
;
16 using namespace llvm::jitlink::aarch32
;
17 using namespace llvm::support
;
18 using namespace llvm::support::endian
;
20 struct MutableHalfWords
{
21 MutableHalfWords(HalfWords Preset
) : Hi(Preset
.Hi
), Lo(Preset
.Lo
) {}
23 void patch(HalfWords Value
, HalfWords Mask
) {
24 Hi
= (Hi
& ~Mask
.Hi
) | Value
.Hi
;
25 Lo
= (Lo
& ~Mask
.Lo
) | Value
.Lo
;
28 uint16_t Hi
; // First halfword
29 uint16_t Lo
; // Second halfword
33 MutableWord(uint32_t Preset
) : Wd(Preset
) {}
35 void patch(uint32_t Value
, uint32_t Mask
) { Wd
= (Wd
& ~Mask
) | Value
; }
42 Expected
<aarch32::EdgeKind_aarch32
>
43 getJITLinkEdgeKind(uint32_t ELFType
, const aarch32::ArmConfig
&Cfg
);
44 Expected
<uint32_t> getELFRelocationType(Edge::Kind Kind
);
46 } // namespace jitlink
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()));
55 // Fails: Invalid JITLink kind -> ELF type
56 Expected
<uint32_t> ErrType
= getELFRelocationType(Edge::Invalid
);
57 EXPECT_TRUE(errorToBool(ErrType
.takeError()));
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";
64 Expected
<Edge::Kind
> JITLinkKind
= getJITLinkEdgeKind(*ELFType
, Cfg
);
65 EXPECT_FALSE(errorToBool(JITLinkKind
.takeError()))
66 << "Failed to translate ELF type -> JITLink kind";
68 EXPECT_EQ(*JITLinkKind
, K
) << "Round-trip value inconsistent?";
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));
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));
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
),
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
);
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
);
115 } // namespace aarch32
116 } // namespace jitlink
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
>
124 static HalfWords
makeHalfWords(std::array
<uint8_t, 4> Mem
) {
125 return HalfWords
{read16
<Endian
>(Mem
.data()), read16
<Endian
>(Mem
.data() + 2)};
128 /// 25-bit branch with link (with J1J2 range extension)
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");
135 constexpr HalfWords ImmMask
= FixupInfo
<Thumb_Call
>::ImmMask
;
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
143 auto EncodeDecode
= [ImmMask
](int64_t In
, MutableHalfWords
&Mem
) {
144 Mem
.patch(encodeImmBT4BlT1BlxT2_J1J2(In
), ImmMask
);
145 return decodeImmBT4BlT1BlxT2_J1J2(Mem
.Hi
, Mem
.Lo
);
148 for (MutableHalfWords Mem
: MemPresets
) {
149 HalfWords
UnaffectedBits(Mem
.Hi
& ~ImmMask
.Hi
, Mem
.Lo
& ~ImmMask
.Lo
);
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
158 EXPECT_TRUE(UnaffectedBits
.Hi
== (Mem
.Hi
& ~ImmMask
.Hi
) &&
159 UnaffectedBits
.Lo
== (Mem
.Lo
& ~ImmMask
.Lo
))
160 << "Diff outside immediate field";
164 /// 22-bit branch with link (without J1J2 range extension)
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");
171 constexpr HalfWords ImmMask
= FixupInfo
<Thumb_Call
>::ImmMask
;
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
179 auto EncodeDecode
= [ImmMask
](int64_t In
, MutableHalfWords
&Mem
) {
180 Mem
.patch(encodeImmBT4BlT1BlxT2_J1J2(In
), ImmMask
);
181 return decodeImmBT4BlT1BlxT2_J1J2(Mem
.Hi
, Mem
.Lo
);
184 for (MutableHalfWords Mem
: MemPresets
) {
185 HalfWords
UnaffectedBits(Mem
.Hi
& ~ImmMask
.Hi
, Mem
.Lo
& ~ImmMask
.Lo
);
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
194 EXPECT_TRUE(UnaffectedBits
.Hi
== (Mem
.Hi
& ~ImmMask
.Hi
) &&
195 UnaffectedBits
.Lo
== (Mem
.Lo
& ~ImmMask
.Lo
))
196 << "Diff outside immediate field";
200 /// 26-bit branch with link
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");
207 constexpr uint32_t ImmMask
= FixupInfo
<Arm_Call
>::ImmMask
;
209 static std::array
<uint32_t, 3> MemPresets
{
210 0xfeeffff7, // common
215 auto EncodeDecode
= [=](int64_t In
, MutableWord
&Mem
) {
216 Mem
.patch(encodeImmBA1BlA1BlxA2(In
), ImmMask
);
217 return decodeImmBA1BlA1BlxA2(Mem
.Wd
);
220 for (MutableWord Mem
: MemPresets
) {
221 MutableWord
UnaffectedBits(Mem
.Wd
& ~ImmMask
);
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
230 EXPECT_TRUE(UnaffectedBits
.Wd
== (Mem
.Wd
& ~ImmMask
))
231 << "Diff outside immediate field";
235 /// Write immediate value to the top halfword of the destination register
236 TEST(AArch32_Relocations
, Thumb_MovtAbs
) {
237 static_assert(isUInt
<16>(65535), "Max value");
238 static_assert(!isUInt
<16>(65536), "First overflow");
240 constexpr HalfWords ImmMask
= FixupInfo
<Thumb_MovtAbs
>::ImmMask
;
241 constexpr HalfWords RegMask
= FixupInfo
<Thumb_MovtAbs
>::RegMask
;
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
250 auto EncodeDecode
= [ImmMask
](uint32_t In
, MutableHalfWords
&Mem
) {
251 Mem
.patch(encodeImmMovtT1MovwT3(In
), ImmMask
);
252 return decodeImmMovtT1MovwT3(Mem
.Hi
, Mem
.Lo
);
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
));
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
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";
275 /// Write immediate value to the top halfword of the destination register
276 TEST(AArch32_Relocations
, Arm_MovtAbs
) {
277 static_assert(isUInt
<16>(65535), "Max value");
278 static_assert(!isUInt
<16>(65536), "First overflow");
280 constexpr uint32_t ImmMask
= FixupInfo
<Arm_MovtAbs
>::ImmMask
;
281 constexpr uint32_t RegMask
= FixupInfo
<Arm_MovtAbs
>::RegMask
;
283 static std::array
<uint8_t, 3> Registers
{0, 5, 12};
284 static std::array
<uint32_t, 3> MemPresets
{
285 0xfeeffff7, // common
290 auto EncodeDecode
= [=](uint64_t In
, MutableWord
&Mem
) {
291 Mem
.patch(encodeImmMovtA1MovwA2(In
), ImmMask
);
292 return decodeImmMovtA1MovwA2(Mem
.Wd
);
295 for (MutableWord Mem
: MemPresets
) {
296 for (uint8_t Reg
: Registers
) {
297 MutableWord
UnaffectedBits(Mem
.Wd
& ~(ImmMask
| RegMask
));
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
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";