1/** 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include <gtest/gtest.h> 17#include "mem/code_allocator.h" 18#include "mem/pool_manager.h" 19#include "target/aarch64/target.h" 20 21#include "vixl_exec_module.h" 22 23namespace panda::compiler::tests { 24 25class EncoderArm64VixlTest : public ::testing::Test { 26public: 27 EncoderArm64VixlTest() 28 { 29 panda::mem::MemConfig::Initialize(64_MB, 64_MB, 64_MB, 32_MB); 30 PoolManager::Initialize(); 31 allocator_ = new ArenaAllocator(SpaceType::SPACE_TYPE_COMPILER); 32 encoder_ = static_cast<aarch64::Aarch64Encoder *>(Encoder::Create(allocator_, Arch::AARCH64, false)); 33 encoder_->InitMasm(); 34 encoder_->SetRegfile(RegistersDescription::Create(allocator_, Arch::AARCH64)); 35 36 exec_module_ = allocator_->New<VixlExecModule>(allocator_, nullptr); 37 } 38 39 ~EncoderArm64VixlTest() 40 { 41 exec_module_->~VixlExecModule(); 42 Logger::Destroy(); 43 encoder_->~Aarch64Encoder(); 44 delete allocator_; 45 PoolManager::Finalize(); 46 panda::mem::MemConfig::Finalize(); 47 } 48 49 auto GetEncoder() 50 { 51 return encoder_; 52 } 53 54 auto GetAllocator() 55 { 56 return allocator_; 57 } 58 59 void Dump(bool enabled) 60 { 61 if (enabled) { 62 auto size = GetEncoder()->GetCursorOffset(); 63 for (uint32_t i = 0; i < size;) { 64 i = encoder_->DisasmInstr(std::cout, i, 0); 65 std::cout << std::endl; 66 } 67 } 68 } 69 70 template <typename T> 71 void TestPcRelativeLoad(size_t data_size, ssize_t offset, bool get_address, 72 std::initializer_list<const char *> insts) 73 { 74 auto buffer = GetEncoder()->GetMasm()->GetBuffer(); 75 T *data = reinterpret_cast<T *>(buffer->template GetOffsetAddress<uint8_t *>(data_size) + offset); 76 T good_value = GetGoodValue<T>(); 77 T bad_value = GetBadValue<T>(); 78 79 { 80 T tmp_value; 81 memcpy_s(&tmp_value, sizeof(T), data, sizeof(T)); 82 ASSERT_EQ(tmp_value, bad_value); 83 } 84 85 // We use memcpy here (instead of just assigning), because the data can be aligned by 4 for 64-bit target, and 86 // asan will complain about this. 87 memcpy_s(data, sizeof(T), &good_value, sizeof(T)); 88 89 GetEncoder()->SetCodeOffset(0); 90 91 buffer->Rewind(data_size); 92 Reg reg(0, TypeInfo(T(0))); 93 if (get_address) { 94 Reg addr(1, INT64_TYPE); 95 GetEncoder()->LoadPcRelative(reg, offset, addr); 96 } else { 97 GetEncoder()->LoadPcRelative(reg, offset); 98 } 99 GetEncoder()->EncodeReturn(); 100 101 auto start_addr = buffer->GetOffsetAddress<const char *>(data_size); 102 exec_module_->SetInstructions(start_addr, buffer->GetEndAddress<const char *>()); 103 exec_module_->Execute(); 104 105 { 106 T tmp_value; 107 memcpy_s(&tmp_value, sizeof(T), data, sizeof(T)); 108 ASSERT_EQ(tmp_value, good_value); 109 } 110 111 { 112 auto inst = insts.begin(); 113 for (uint32_t i = data_size; i < buffer->GetCursorOffset() && inst != insts.end(); ++inst) { 114 std::stringstream ss; 115 i = encoder_->DisasmInstr(ss, i, 0); 116 auto pos = ss.str().find(*inst); 117 EXPECT_NE(pos, std::string::npos) << *inst << " not found"; 118 if (pos == std::string::npos) { 119 return; 120 } 121 } 122 } 123 124 memcpy_s(data, sizeof(T), &bad_value, sizeof(T)); 125 126 if (get_address) { 127 EXPECT_EQ(exec_module_->GetSimulator()->ReadXRegister(1), reinterpret_cast<int64_t>(data)); 128 } 129 EXPECT_EQ(exec_module_->GetRetValue(), GetGoodValue<T>()); 130 } 131 132 void TestOffset(size_t data_size, ssize_t offset) 133 { 134 ASSERT((offset & 3) == 0); 135 if (vixl::IsInt21(offset)) { 136 TestPcRelativeLoad<uint64_t>(data_size, offset, false, {"adr", "ldr"}); 137 TestPcRelativeLoad<uint64_t>(data_size, offset, true, {"adr", "ldr"}); 138 TestPcRelativeLoad<uint64_t>(data_size, -offset, false, {"adr", "ldr"}); 139 TestPcRelativeLoad<uint64_t>(data_size, -offset, true, {"adr", "ldr"}); 140 TestPcRelativeLoad<uint32_t>(data_size, offset, false, {"adr", "ldr"}); 141 TestPcRelativeLoad<uint32_t>(data_size, offset, true, {"adr", "ldr"}); 142 TestPcRelativeLoad<uint32_t>(data_size, -offset, false, {"adr", "ldr"}); 143 TestPcRelativeLoad<uint32_t>(data_size, -offset, true, {"adr", "ldr"}); 144 } else { 145 if ((offset & 7) == 0) { 146 TestPcRelativeLoad<uint64_t>(data_size, offset, false, {"adrp", "ldr"}); 147 TestPcRelativeLoad<uint64_t>(data_size, offset, true, {"adrp", "add", "ldr"}); 148 TestPcRelativeLoad<uint64_t>(data_size, -offset, false, {"adrp", "ldr"}); 149 TestPcRelativeLoad<uint64_t>(data_size, -offset, true, {"adrp", "add", "ldr"}); 150 } else { 151 TestPcRelativeLoad<uint64_t>(data_size, offset, false, {"adrp", "mov", "ldr"}); 152 TestPcRelativeLoad<uint64_t>(data_size, offset, true, {"adrp", "add", "ldr"}); 153 TestPcRelativeLoad<uint64_t>(data_size, -offset, false, {"adrp", "mov", "ldr"}); 154 TestPcRelativeLoad<uint64_t>(data_size, -offset, true, {"adrp", "add", "ldr"}); 155 } 156 TestPcRelativeLoad<uint32_t>(data_size, offset, false, {"adrp", "ldr"}); 157 TestPcRelativeLoad<uint32_t>(data_size, offset, true, {"adrp", "add", "ldr"}); 158 TestPcRelativeLoad<uint32_t>(data_size, -offset, false, {"adrp", "ldr"}); 159 TestPcRelativeLoad<uint32_t>(data_size, -offset, true, {"adrp", "add", "ldr"}); 160 } 161 } 162 163 template <typename T> 164 static constexpr T GetBadValue() 165 { 166 if constexpr (sizeof(T) == sizeof(uint64_t)) { 167 return BAD_VALUE64; 168 } 169 return BAD_VALUE; 170 } 171 172 template <typename T> 173 static constexpr T GetGoodValue() 174 { 175 if constexpr (sizeof(T) == sizeof(uint64_t)) { 176 return GOOD_VALUE64; 177 } 178 return GOOD_VALUE; 179 } 180 181protected: 182 static constexpr uint32_t BAD_VALUE = 0xdeadbaad; 183 static constexpr uint32_t GOOD_VALUE = 0xcafebabe; 184 static constexpr uint64_t BAD_VALUE64 = 0xdeadbaaddeadbaad; 185 static constexpr uint64_t GOOD_VALUE64 = 0x1234cafebabe4321; 186 static constexpr uint64_t MAX_INT21_VALUE = 0x100000; 187 188private: 189 ArenaAllocator *allocator_ {nullptr}; 190 aarch64::Aarch64Encoder *encoder_ {nullptr}; 191 VixlExecModule *exec_module_ {nullptr}; 192}; 193 194TEST_F(EncoderArm64VixlTest, LoadPcRelative) 195{ 196 auto encoder = GetEncoder(); 197 auto masm = encoder->GetMasm(); 198 auto buffer = masm->GetBuffer(); 199 static constexpr size_t code_offset = 16; 200 static constexpr size_t buf_size = (MAX_INT21_VALUE - vixl::aarch64::kPageSize) * 2 + code_offset; 201 202 for (size_t i = 0; i < buf_size / sizeof(uint32_t); i++) { 203 buffer->Emit32(GetBadValue<uint32_t>()); 204 } 205 // Pre-allocate space for the code 206 for (size_t i = 0; i < 4; i++) { 207 buffer->Emit32(0); 208 } 209 for (size_t i = 0; i < buf_size / sizeof(uint32_t); i++) { 210 buffer->Emit32(GetBadValue<uint32_t>()); 211 } 212 213 TestOffset(buf_size, 40); 214 TestOffset(buf_size, 44); 215 TestOffset(buf_size, 0x1374b8); 216 // Check for two pages addrp 217 TestOffset(buf_size, MAX_INT21_VALUE + vixl::aarch64::kPageSize + code_offset + 8); 218 // Check for one page addrp 219 TestOffset(buf_size, MAX_INT21_VALUE + vixl::aarch64::kPageSize + code_offset - 8); 220 TestOffset(buf_size, 0x100404); 221} 222 223} // namespace panda::compiler::tests 224