brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.1 KiB · dfabb4a Raw
313 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include <llvm/BinaryFormat/ELF.h>10#include <llvm/ExecutionEngine/JITLink/aarch32.h>11#include <llvm/Support/Compiler.h>12 13#include "gtest/gtest.h"14 15using namespace llvm;16using namespace llvm::jitlink;17using namespace llvm::jitlink::aarch32;18using namespace llvm::support;19using namespace llvm::support::endian;20 21struct MutableHalfWords {22  MutableHalfWords(HalfWords Preset) : Hi(Preset.Hi), Lo(Preset.Lo) {}23 24  void patch(HalfWords Value, HalfWords Mask) {25    Hi = (Hi & ~Mask.Hi) | Value.Hi;26    Lo = (Lo & ~Mask.Lo) | Value.Lo;27  }28 29  uint16_t Hi; // First halfword30  uint16_t Lo; // Second halfword31};32 33struct MutableWord {34  MutableWord(uint32_t Preset) : Wd(Preset) {}35 36  void patch(uint32_t Value, uint32_t Mask) { Wd = (Wd & ~Mask) | Value; }37 38  uint32_t Wd;39};40namespace llvm {41namespace jitlink {42 43Expected<aarch32::EdgeKind_aarch32>44getJITLinkEdgeKind(uint32_t ELFType, const aarch32::ArmConfig &Cfg);45Expected<uint32_t> getELFRelocationType(Edge::Kind Kind);46 47} // namespace jitlink48} // namespace llvm49 50TEST(AArch32_ELF, EdgeKinds) {51  // Fails: Invalid ELF type -> JITLink kind52  aarch32::ArmConfig Cfg;53  Expected<uint32_t> ErrKind = getJITLinkEdgeKind(ELF::R_ARM_ME_TOO, Cfg);54  EXPECT_TRUE(errorToBool(ErrKind.takeError()));55 56  // Fails: Invalid JITLink kind -> ELF type57  Expected<uint32_t> ErrType = getELFRelocationType(Edge::Invalid);58  EXPECT_TRUE(errorToBool(ErrType.takeError()));59 60  for (Edge::Kind K = FirstDataRelocation; K < LastThumbRelocation; K += 1) {61    Expected<uint32_t> ELFType = getELFRelocationType(K);62    EXPECT_FALSE(errorToBool(ELFType.takeError()))63        << "Failed to translate JITLink kind -> ELF type";64 65    Expected<Edge::Kind> JITLinkKind = getJITLinkEdgeKind(*ELFType, Cfg);66    EXPECT_FALSE(errorToBool(JITLinkKind.takeError()))67        << "Failed to translate ELF type -> JITLink kind";68 69    EXPECT_EQ(*JITLinkKind, K) << "Round-trip value inconsistent?";70  }71}72 73TEST(AArch32_ELF, DynFixupInfos) {74  // We can do an opcode check for all Arm edges75  for (Edge::Kind K = FirstArmRelocation; K < LastArmRelocation; K += 1) {76    const auto *Info = FixupInfoBase::getDynFixupInfo(K);77    EXPECT_NE(Info, nullptr);78    const auto *InfoArm = static_cast<const FixupInfoArm *>(Info);79    EXPECT_NE(InfoArm->checkOpcode, nullptr);80    EXPECT_FALSE(InfoArm->checkOpcode(0x00000000));81  }82  // We can do an opcode check for all Thumb edges83  for (Edge::Kind K = FirstThumbRelocation; K < LastThumbRelocation; K += 1) {84    const auto *Info = FixupInfoBase::getDynFixupInfo(K);85    EXPECT_NE(Info, nullptr);86    const auto *InfoThumb = static_cast<const FixupInfoThumb *>(Info);87    EXPECT_NE(InfoThumb->checkOpcode, nullptr);88    EXPECT_FALSE(InfoThumb->checkOpcode(0x0000, 0x0000));89  }90  // We cannot do it for Data and generic edges91  EXPECT_EQ(FixupInfoBase::getDynFixupInfo(FirstDataRelocation), nullptr);92  EXPECT_EQ(FixupInfoBase::getDynFixupInfo(Edge::GenericEdgeKind::Invalid),93            nullptr);94}95 96namespace llvm {97namespace jitlink {98namespace aarch32 {99 100LLVM_ABI HalfWords encodeImmBT4BlT1BlxT2(int64_t Value);101LLVM_ABI HalfWords encodeImmBT4BlT1BlxT2_J1J2(int64_t Value);102LLVM_ABI uint32_t encodeImmBA1BlA1BlxA2(int64_t Value);103LLVM_ABI HalfWords encodeImmMovtT1MovwT3(uint16_t Value);104LLVM_ABI HalfWords encodeRegMovtT1MovwT3(int64_t Value);105LLVM_ABI uint32_t encodeImmMovtA1MovwA2(uint16_t Value);106LLVM_ABI uint32_t encodeRegMovtA1MovwA2(int64_t Value);107 108LLVM_ABI int64_t decodeImmBT4BlT1BlxT2(uint32_t Hi, uint32_t Lo);109LLVM_ABI int64_t decodeImmBT4BlT1BlxT2_J1J2(uint32_t Hi, uint32_t Lo);110LLVM_ABI int64_t decodeImmBA1BlA1BlxA2(int64_t Value);111LLVM_ABI uint16_t decodeImmMovtT1MovwT3(uint32_t Hi, uint32_t Lo);112LLVM_ABI int64_t decodeRegMovtT1MovwT3(uint32_t Hi, uint32_t Lo);113LLVM_ABI uint16_t decodeImmMovtA1MovwA2(uint64_t Value);114LLVM_ABI int64_t decodeRegMovtA1MovwA2(uint64_t Value);115 116} // namespace aarch32117} // namespace jitlink118} // namespace llvm119 120// Big-endian for v7 and v8 (and v6 unless in legacy backwards compatible mode121// be32) have little-endian instructions and big-endian data. In ELF relocatable122// objects big-endian instructions may still be encountered. A be8 supporting123// linker is expected to endian-reverse instructions for the executable.124template <endianness Endian>125static HalfWords makeHalfWords(std::array<uint8_t, 4> Mem) {126  return HalfWords{read16<Endian>(Mem.data()), read16<Endian>(Mem.data() + 2)};127}128 129/// 25-bit branch with link (with J1J2 range extension)130TEST(AArch32_Relocations, Thumb_Call_J1J2) {131  static_assert(isInt<25>(16777215), "Max value");132  static_assert(isInt<25>(-16777215), "Min value");133  static_assert(!isInt<25>(16777217), "First overflow");134  static_assert(!isInt<25>(-16777217), "First underflow");135 136  constexpr HalfWords ImmMask = FixupInfo<Thumb_Call>::ImmMask;137 138  static std::array<HalfWords, 3> MemPresets{139      makeHalfWords<endianness::little>({0xff, 0xf7, 0xfe, 0xef}), // common140      makeHalfWords<endianness::little>({0x00, 0x00, 0x00, 0x00}), // zeros141      makeHalfWords<endianness::little>({0xff, 0xff, 0xff, 0xff}), // ones142  };143 144  auto EncodeDecode = [ImmMask](int64_t In, MutableHalfWords &Mem) {145    Mem.patch(encodeImmBT4BlT1BlxT2_J1J2(In), ImmMask);146    return decodeImmBT4BlT1BlxT2_J1J2(Mem.Hi, Mem.Lo);147  };148 149  for (MutableHalfWords Mem : MemPresets) {150    HalfWords UnaffectedBits(Mem.Hi & ~ImmMask.Hi, Mem.Lo & ~ImmMask.Lo);151 152    EXPECT_EQ(EncodeDecode(1, Mem), 0);                 // Zero value153    EXPECT_EQ(EncodeDecode(0x41, Mem), 0x40);           // Common value154    EXPECT_EQ(EncodeDecode(16777215, Mem), 16777214);   // Maximum value155    EXPECT_EQ(EncodeDecode(-16777215, Mem), -16777216); // Minimum value156    EXPECT_NE(EncodeDecode(16777217, Mem), 16777217);   // First overflow157    EXPECT_NE(EncodeDecode(-16777217, Mem), -16777217); // First underflow158 159    EXPECT_TRUE(UnaffectedBits.Hi == (Mem.Hi & ~ImmMask.Hi) &&160                UnaffectedBits.Lo == (Mem.Lo & ~ImmMask.Lo))161        << "Diff outside immediate field";162  }163}164 165/// 22-bit branch with link (without J1J2 range extension)166TEST(AArch32_Relocations, Thumb_Call_Bare) {167  static_assert(isInt<22>(2097151), "Max value");168  static_assert(isInt<22>(-2097151), "Min value");169  static_assert(!isInt<22>(2097153), "First overflow");170  static_assert(!isInt<22>(-2097153), "First underflow");171 172  constexpr HalfWords ImmMask = FixupInfo<Thumb_Call>::ImmMask;173 174  static std::array<HalfWords, 3> MemPresets{175      makeHalfWords<endianness::little>({0xff, 0xf7, 0xfe, 0xef}), // common176      makeHalfWords<endianness::little>({0x00, 0x00, 0x00, 0x00}), // zeros177      makeHalfWords<endianness::little>({0xff, 0xff, 0xff, 0xff}), // ones178  };179 180  auto EncodeDecode = [ImmMask](int64_t In, MutableHalfWords &Mem) {181    Mem.patch(encodeImmBT4BlT1BlxT2_J1J2(In), ImmMask);182    return decodeImmBT4BlT1BlxT2_J1J2(Mem.Hi, Mem.Lo);183  };184 185  for (MutableHalfWords Mem : MemPresets) {186    HalfWords UnaffectedBits(Mem.Hi & ~ImmMask.Hi, Mem.Lo & ~ImmMask.Lo);187 188    EXPECT_EQ(EncodeDecode(1, Mem), 0);               // Zero value189    EXPECT_EQ(EncodeDecode(0x41, Mem), 0x40);         // Common value190    EXPECT_EQ(EncodeDecode(2097151, Mem), 2097150);   // Maximum value191    EXPECT_EQ(EncodeDecode(-2097151, Mem), -2097152); // Minimum value192    EXPECT_NE(EncodeDecode(2097153, Mem), 2097153);   // First overflow193    EXPECT_NE(EncodeDecode(-2097153, Mem), -2097153); // First underflow194 195    EXPECT_TRUE(UnaffectedBits.Hi == (Mem.Hi & ~ImmMask.Hi) &&196                UnaffectedBits.Lo == (Mem.Lo & ~ImmMask.Lo))197        << "Diff outside immediate field";198  }199}200 201/// 26-bit branch with link202TEST(AArch32_Relocations, Arm_Call_Bare) {203  static_assert(isInt<26>(33554430), "Max value");204  static_assert(isInt<26>(-33554432), "Min value");205  static_assert(!isInt<26>(33554432), "First overflow");206  static_assert(!isInt<26>(-33554434), "First underflow");207 208  constexpr uint32_t ImmMask = FixupInfo<Arm_Call>::ImmMask;209 210  static std::array<uint32_t, 3> MemPresets{211      0xfeeffff7, // common212      0x00000000, // zeros213      0xffffffff, // ones214  };215 216  auto EncodeDecode = [=](int64_t In, MutableWord &Mem) {217    Mem.patch(encodeImmBA1BlA1BlxA2(In), ImmMask);218    return decodeImmBA1BlA1BlxA2(Mem.Wd);219  };220 221  for (MutableWord Mem : MemPresets) {222    MutableWord UnaffectedBits(Mem.Wd & ~ImmMask);223 224    EXPECT_EQ(EncodeDecode(0, Mem), 0);                 // Zero value225    EXPECT_EQ(EncodeDecode(0x40, Mem), 0x40);           // Common value226    EXPECT_EQ(EncodeDecode(33554428, Mem), 33554428);   // Maximum value227    EXPECT_EQ(EncodeDecode(-33554432, Mem), -33554432); // Minimum value228    EXPECT_NE(EncodeDecode(33554434, Mem), 33554434);   // First overflow229    EXPECT_NE(EncodeDecode(-33554434, Mem), -33554434); // First underflow230 231    EXPECT_TRUE(UnaffectedBits.Wd == (Mem.Wd & ~ImmMask))232        << "Diff outside immediate field";233  }234}235 236/// Write immediate value to the top halfword of the destination register237TEST(AArch32_Relocations, Thumb_MovtAbs) {238  static_assert(isUInt<16>(65535), "Max value");239  static_assert(!isUInt<16>(65536), "First overflow");240 241  constexpr HalfWords ImmMask = FixupInfo<Thumb_MovtAbs>::ImmMask;242  constexpr HalfWords RegMask = FixupInfo<Thumb_MovtAbs>::RegMask;243 244  static std::array<uint8_t, 3> Registers{0, 5, 12};245  static std::array<HalfWords, 3> MemPresets{246      makeHalfWords<endianness::little>({0xff, 0xf7, 0xfe, 0xef}), // common247      makeHalfWords<endianness::little>({0x00, 0x00, 0x00, 0x00}), // zeros248      makeHalfWords<endianness::little>({0xff, 0xff, 0xff, 0xff}), // ones249  };250 251  auto EncodeDecode = [ImmMask](uint32_t In, MutableHalfWords &Mem) {252    Mem.patch(encodeImmMovtT1MovwT3(In), ImmMask);253    return decodeImmMovtT1MovwT3(Mem.Hi, Mem.Lo);254  };255 256  for (MutableHalfWords Mem : MemPresets) {257    for (uint8_t Reg : Registers) {258      HalfWords UnaffectedBits(Mem.Hi & ~(ImmMask.Hi | RegMask.Hi),259                               Mem.Lo & ~(ImmMask.Lo | RegMask.Lo));260 261      Mem.patch(encodeRegMovtT1MovwT3(Reg), RegMask);262      EXPECT_EQ(EncodeDecode(0x76bb, Mem), 0x76bb);   // Common value263      EXPECT_EQ(EncodeDecode(0, Mem), 0);             // Minimum value264      EXPECT_EQ(EncodeDecode(0xffff, Mem), 0xffff);   // Maximum value265      EXPECT_NE(EncodeDecode(0x10000, Mem), 0x10000); // First overflow266 267      // Destination register as well as unaffected bits should be intact268      EXPECT_EQ(decodeRegMovtT1MovwT3(Mem.Hi, Mem.Lo), Reg);269      EXPECT_TRUE(UnaffectedBits.Hi == (Mem.Hi & ~(ImmMask.Hi | RegMask.Hi)) &&270                  UnaffectedBits.Lo == (Mem.Lo & ~(ImmMask.Lo | RegMask.Lo)))271          << "Diff outside immediate/register field";272    }273  }274}275 276/// Write immediate value to the top halfword of the destination register277TEST(AArch32_Relocations, Arm_MovtAbs) {278  static_assert(isUInt<16>(65535), "Max value");279  static_assert(!isUInt<16>(65536), "First overflow");280 281  constexpr uint32_t ImmMask = FixupInfo<Arm_MovtAbs>::ImmMask;282  constexpr uint32_t RegMask = FixupInfo<Arm_MovtAbs>::RegMask;283 284  static std::array<uint8_t, 3> Registers{0, 5, 12};285  static std::array<uint32_t, 3> MemPresets{286      0xfeeffff7, // common287      0x00000000, // zeros288      0xffffffff, // ones289  };290 291  auto EncodeDecode = [=](uint64_t In, MutableWord &Mem) {292    Mem.patch(encodeImmMovtA1MovwA2(In), ImmMask);293    return decodeImmMovtA1MovwA2(Mem.Wd);294  };295 296  for (MutableWord Mem : MemPresets) {297    for (uint8_t Reg : Registers) {298      MutableWord UnaffectedBits(Mem.Wd & ~(ImmMask | RegMask));299 300      Mem.patch(encodeRegMovtA1MovwA2(Reg), RegMask);301      EXPECT_EQ(EncodeDecode(0x76bb, Mem), 0x76bb);   // Common value302      EXPECT_EQ(EncodeDecode(0, Mem), 0);             // Minimum value303      EXPECT_EQ(EncodeDecode(0xffff, Mem), 0xffff);   // Maximum value304      EXPECT_NE(EncodeDecode(0x10000, Mem), 0x10000); // First overflow305 306      // Destination register as well as unaffected bits should be intact307      EXPECT_EQ(decodeRegMovtA1MovwA2(Mem.Wd), Reg);308      EXPECT_TRUE(UnaffectedBits.Wd == (Mem.Wd & ~(ImmMask | RegMask)))309          << "Diff outside immediate/register field";310    }311  }312}313