1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <dirent.h> 18 #include <errno.h> 19 #include <fstream> 20 #include <map> 21 #include <string.h> 22 #include <sys/types.h> 23 24 #include "gtest/gtest.h" 25 26 #include "jni/quick/calling_convention.h" 27 #include "utils/arm/jni_macro_assembler_arm_vixl.h" 28 29 #include "base/hex_dump.h" 30 #include "common_runtime_test.h" 31 32 namespace art { 33 namespace arm { 34 35 // Include results file (generated manually) 36 #include "assembler_thumb_test_expected.cc.inc" 37 38 #ifndef ART_TARGET_ANDROID 39 // This controls whether the results are printed to the 40 // screen or compared against the expected output. 41 // To generate new expected output, set this to true and 42 // copy the output into the .cc.inc file in the form 43 // of the other results. 44 // 45 // When this is false, the results are not printed to the 46 // output, but are compared against the expected results 47 // in the .cc.inc file. 48 static constexpr bool kPrintResults = false; 49 #endif 50 51 void SetAndroidData() { 52 const char* data = getenv("ANDROID_DATA"); 53 if (data == nullptr) { 54 setenv("ANDROID_DATA", "/tmp", 1); 55 } 56 } 57 58 int CompareIgnoringSpace(const char* s1, const char* s2) { 59 while (*s1 != '\0') { 60 while (isspace(*s1)) ++s1; 61 while (isspace(*s2)) ++s2; 62 if (*s1 == '\0' || *s1 != *s2) { 63 break; 64 } 65 ++s1; 66 ++s2; 67 } 68 return *s1 - *s2; 69 } 70 71 void InitResults() { 72 if (test_results.empty()) { 73 setup_results(); 74 } 75 } 76 77 std::string GetToolsDir() { 78 #ifndef ART_TARGET_ANDROID 79 // This will only work on the host. There is no as, objcopy or objdump on the device. 80 static std::string toolsdir; 81 82 if (toolsdir.empty()) { 83 setup_results(); 84 toolsdir = CommonRuntimeTest::GetAndroidTargetToolsDir(kThumb2); 85 SetAndroidData(); 86 } 87 88 return toolsdir; 89 #else 90 return std::string(); 91 #endif 92 } 93 94 void DumpAndCheck(std::vector<uint8_t>& code, const char* testname, const char* const* results) { 95 #ifndef ART_TARGET_ANDROID 96 static std::string toolsdir = GetToolsDir(); 97 98 ScratchFile file; 99 100 const char* filename = file.GetFilename().c_str(); 101 102 std::ofstream out(filename); 103 if (out) { 104 out << ".section \".text\"\n"; 105 out << ".syntax unified\n"; 106 out << ".arch armv7-a\n"; 107 out << ".thumb\n"; 108 out << ".thumb_func\n"; 109 out << ".type " << testname << ", #function\n"; 110 out << ".global " << testname << "\n"; 111 out << testname << ":\n"; 112 out << ".fnstart\n"; 113 114 for (uint32_t i = 0 ; i < code.size(); ++i) { 115 out << ".byte " << (static_cast<int>(code[i]) & 0xff) << "\n"; 116 } 117 out << ".fnend\n"; 118 out << ".size " << testname << ", .-" << testname << "\n"; 119 } 120 out.close(); 121 122 char cmd[1024]; 123 124 // Assemble the .S 125 snprintf(cmd, sizeof(cmd), "%sas %s -o %s.o", toolsdir.c_str(), filename, filename); 126 int cmd_result = system(cmd); 127 ASSERT_EQ(cmd_result, 0) << strerror(errno); 128 129 // Remove the $d symbols to prevent the disassembler dumping the instructions 130 // as .word 131 snprintf(cmd, sizeof(cmd), "%sobjcopy -N '$d' %s.o %s.oo", toolsdir.c_str(), filename, filename); 132 int cmd_result2 = system(cmd); 133 ASSERT_EQ(cmd_result2, 0) << strerror(errno); 134 135 // Disassemble. 136 137 snprintf(cmd, sizeof(cmd), "%sobjdump -d %s.oo | grep '^ *[0-9a-f][0-9a-f]*:'", 138 toolsdir.c_str(), filename); 139 if (kPrintResults) { 140 // Print the results only, don't check. This is used to generate new output for inserting 141 // into the .inc file, so let's add the appropriate prefix/suffix needed in the C++ code. 142 strcat(cmd, " | sed '-es/^/ \"/' | sed '-es/$/\\\\n\",/'"); 143 int cmd_result3 = system(cmd); 144 ASSERT_EQ(cmd_result3, 0) << strerror(errno); 145 } else { 146 // Check the results match the appropriate results in the .inc file. 147 FILE *fp = popen(cmd, "r"); 148 ASSERT_TRUE(fp != nullptr); 149 150 uint32_t lineindex = 0; 151 152 while (!feof(fp)) { 153 char testline[256]; 154 char *s = fgets(testline, sizeof(testline), fp); 155 if (s == nullptr) { 156 break; 157 } 158 if (CompareIgnoringSpace(results[lineindex], testline) != 0) { 159 LOG(FATAL) << "Output is not as expected at line: " << lineindex 160 << results[lineindex] << "/" << testline << ", test name: " << testname; 161 } 162 ++lineindex; 163 } 164 // Check that we are at the end. 165 ASSERT_TRUE(results[lineindex] == nullptr); 166 fclose(fp); 167 } 168 169 char buf[FILENAME_MAX]; 170 snprintf(buf, sizeof(buf), "%s.o", filename); 171 unlink(buf); 172 173 snprintf(buf, sizeof(buf), "%s.oo", filename); 174 unlink(buf); 175 #endif // ART_TARGET_ANDROID 176 } 177 178 class ArmVIXLAssemblerTest : public ::testing::Test { 179 public: 180 ArmVIXLAssemblerTest() : pool(), arena(&pool), assembler(&arena) { } 181 182 ArenaPool pool; 183 ArenaAllocator arena; 184 ArmVIXLJNIMacroAssembler assembler; 185 }; 186 187 #define __ assembler-> 188 189 void EmitAndCheck(ArmVIXLJNIMacroAssembler* assembler, const char* testname, 190 const char* const* results) { 191 __ FinalizeCode(); 192 size_t cs = __ CodeSize(); 193 std::vector<uint8_t> managed_code(cs); 194 MemoryRegion code(&managed_code[0], managed_code.size()); 195 __ FinalizeInstructions(code); 196 197 DumpAndCheck(managed_code, testname, results); 198 } 199 200 void EmitAndCheck(ArmVIXLJNIMacroAssembler* assembler, const char* testname) { 201 InitResults(); 202 std::map<std::string, const char* const*>::iterator results = test_results.find(testname); 203 ASSERT_NE(results, test_results.end()); 204 205 EmitAndCheck(assembler, testname, results->second); 206 } 207 208 #undef __ 209 210 #define __ assembler. 211 212 TEST_F(ArmVIXLAssemblerTest, VixlJniHelpers) { 213 // Run the test only with Baker read barriers, as the expected 214 // generated code contains a Marking Register refresh instruction. 215 TEST_DISABLED_WITHOUT_BAKER_READ_BARRIERS(); 216 217 const bool is_static = true; 218 const bool is_synchronized = false; 219 const bool is_critical_native = false; 220 const char* shorty = "IIFII"; 221 222 ArenaPool pool; 223 ArenaAllocator arena(&pool); 224 225 std::unique_ptr<JniCallingConvention> jni_conv( 226 JniCallingConvention::Create(&arena, 227 is_static, 228 is_synchronized, 229 is_critical_native, 230 shorty, 231 kThumb2)); 232 std::unique_ptr<ManagedRuntimeCallingConvention> mr_conv( 233 ManagedRuntimeCallingConvention::Create(&arena, is_static, is_synchronized, shorty, kThumb2)); 234 const int frame_size(jni_conv->FrameSize()); 235 ArrayRef<const ManagedRegister> callee_save_regs = jni_conv->CalleeSaveRegisters(); 236 237 const ManagedRegister method_register = ArmManagedRegister::FromCoreRegister(R0); 238 const ManagedRegister scratch_register = ArmManagedRegister::FromCoreRegister(R12); 239 240 __ BuildFrame(frame_size, mr_conv->MethodRegister(), callee_save_regs, mr_conv->EntrySpills()); 241 __ IncreaseFrameSize(32); 242 243 // Loads 244 __ IncreaseFrameSize(4096); 245 __ Load(method_register, FrameOffset(32), 4); 246 __ Load(method_register, FrameOffset(124), 4); 247 __ Load(method_register, FrameOffset(132), 4); 248 __ Load(method_register, FrameOffset(1020), 4); 249 __ Load(method_register, FrameOffset(1024), 4); 250 __ Load(scratch_register, FrameOffset(4092), 4); 251 __ Load(scratch_register, FrameOffset(4096), 4); 252 __ LoadRawPtrFromThread(scratch_register, ThreadOffset32(512)); 253 __ LoadRef(method_register, scratch_register, MemberOffset(128), /* unpoison_reference */ false); 254 255 // Stores 256 __ Store(FrameOffset(32), method_register, 4); 257 __ Store(FrameOffset(124), method_register, 4); 258 __ Store(FrameOffset(132), method_register, 4); 259 __ Store(FrameOffset(1020), method_register, 4); 260 __ Store(FrameOffset(1024), method_register, 4); 261 __ Store(FrameOffset(4092), scratch_register, 4); 262 __ Store(FrameOffset(4096), scratch_register, 4); 263 __ StoreImmediateToFrame(FrameOffset(48), 0xFF, scratch_register); 264 __ StoreImmediateToFrame(FrameOffset(48), 0xFFFFFF, scratch_register); 265 __ StoreRawPtr(FrameOffset(48), scratch_register); 266 __ StoreRef(FrameOffset(48), scratch_register); 267 __ StoreSpanning(FrameOffset(48), method_register, FrameOffset(48), scratch_register); 268 __ StoreStackOffsetToThread(ThreadOffset32(512), FrameOffset(4096), scratch_register); 269 __ StoreStackPointerToThread(ThreadOffset32(512)); 270 271 // Other 272 __ Call(method_register, FrameOffset(48), scratch_register); 273 __ Copy(FrameOffset(48), FrameOffset(44), scratch_register, 4); 274 __ CopyRawPtrFromThread(FrameOffset(44), ThreadOffset32(512), scratch_register); 275 __ CopyRef(FrameOffset(48), FrameOffset(44), scratch_register); 276 __ GetCurrentThread(method_register); 277 __ GetCurrentThread(FrameOffset(48), scratch_register); 278 __ Move(scratch_register, method_register, 4); 279 __ VerifyObject(scratch_register, false); 280 281 __ CreateHandleScopeEntry(scratch_register, FrameOffset(48), scratch_register, true); 282 __ CreateHandleScopeEntry(scratch_register, FrameOffset(48), scratch_register, false); 283 __ CreateHandleScopeEntry(method_register, FrameOffset(48), scratch_register, true); 284 __ CreateHandleScopeEntry(FrameOffset(48), FrameOffset(64), scratch_register, true); 285 __ CreateHandleScopeEntry(method_register, FrameOffset(0), scratch_register, true); 286 __ CreateHandleScopeEntry(method_register, FrameOffset(1025), scratch_register, true); 287 __ CreateHandleScopeEntry(scratch_register, FrameOffset(1025), scratch_register, true); 288 289 __ ExceptionPoll(scratch_register, 0); 290 291 // Push the target out of range of branch emitted by ExceptionPoll. 292 for (int i = 0; i < 64; i++) { 293 __ Store(FrameOffset(2047), scratch_register, 4); 294 } 295 296 __ DecreaseFrameSize(4096); 297 __ DecreaseFrameSize(32); 298 __ RemoveFrame(frame_size, callee_save_regs); 299 300 EmitAndCheck(&assembler, "VixlJniHelpers"); 301 } 302 303 #undef __ 304 305 // TODO: Avoid these macros. 306 #define R0 vixl::aarch32::r0 307 #define R2 vixl::aarch32::r2 308 #define R4 vixl::aarch32::r4 309 #define R12 vixl::aarch32::r12 310 311 #define __ assembler.asm_. 312 313 TEST_F(ArmVIXLAssemblerTest, VixlLoadFromOffset) { 314 __ LoadFromOffset(kLoadWord, R2, R4, 12); 315 __ LoadFromOffset(kLoadWord, R2, R4, 0xfff); 316 __ LoadFromOffset(kLoadWord, R2, R4, 0x1000); 317 __ LoadFromOffset(kLoadWord, R2, R4, 0x1000a4); 318 __ LoadFromOffset(kLoadWord, R2, R4, 0x101000); 319 __ LoadFromOffset(kLoadWord, R4, R4, 0x101000); 320 __ LoadFromOffset(kLoadUnsignedHalfword, R2, R4, 12); 321 __ LoadFromOffset(kLoadUnsignedHalfword, R2, R4, 0xfff); 322 __ LoadFromOffset(kLoadUnsignedHalfword, R2, R4, 0x1000); 323 __ LoadFromOffset(kLoadUnsignedHalfword, R2, R4, 0x1000a4); 324 __ LoadFromOffset(kLoadUnsignedHalfword, R2, R4, 0x101000); 325 __ LoadFromOffset(kLoadUnsignedHalfword, R4, R4, 0x101000); 326 __ LoadFromOffset(kLoadWordPair, R2, R4, 12); 327 __ LoadFromOffset(kLoadWordPair, R2, R4, 0x3fc); 328 __ LoadFromOffset(kLoadWordPair, R2, R4, 0x400); 329 __ LoadFromOffset(kLoadWordPair, R2, R4, 0x400a4); 330 __ LoadFromOffset(kLoadWordPair, R2, R4, 0x40400); 331 __ LoadFromOffset(kLoadWordPair, R4, R4, 0x40400); 332 333 vixl::aarch32::UseScratchRegisterScope temps(assembler.asm_.GetVIXLAssembler()); 334 temps.Exclude(R12); 335 __ LoadFromOffset(kLoadWord, R0, R12, 12); // 32-bit because of R12. 336 temps.Include(R12); 337 __ LoadFromOffset(kLoadWord, R2, R4, 0xa4 - 0x100000); 338 339 __ LoadFromOffset(kLoadSignedByte, R2, R4, 12); 340 __ LoadFromOffset(kLoadUnsignedByte, R2, R4, 12); 341 __ LoadFromOffset(kLoadSignedHalfword, R2, R4, 12); 342 343 EmitAndCheck(&assembler, "VixlLoadFromOffset"); 344 } 345 346 TEST_F(ArmVIXLAssemblerTest, VixlStoreToOffset) { 347 __ StoreToOffset(kStoreWord, R2, R4, 12); 348 __ StoreToOffset(kStoreWord, R2, R4, 0xfff); 349 __ StoreToOffset(kStoreWord, R2, R4, 0x1000); 350 __ StoreToOffset(kStoreWord, R2, R4, 0x1000a4); 351 __ StoreToOffset(kStoreWord, R2, R4, 0x101000); 352 __ StoreToOffset(kStoreWord, R4, R4, 0x101000); 353 __ StoreToOffset(kStoreHalfword, R2, R4, 12); 354 __ StoreToOffset(kStoreHalfword, R2, R4, 0xfff); 355 __ StoreToOffset(kStoreHalfword, R2, R4, 0x1000); 356 __ StoreToOffset(kStoreHalfword, R2, R4, 0x1000a4); 357 __ StoreToOffset(kStoreHalfword, R2, R4, 0x101000); 358 __ StoreToOffset(kStoreHalfword, R4, R4, 0x101000); 359 __ StoreToOffset(kStoreWordPair, R2, R4, 12); 360 __ StoreToOffset(kStoreWordPair, R2, R4, 0x3fc); 361 __ StoreToOffset(kStoreWordPair, R2, R4, 0x400); 362 __ StoreToOffset(kStoreWordPair, R2, R4, 0x400a4); 363 __ StoreToOffset(kStoreWordPair, R2, R4, 0x40400); 364 __ StoreToOffset(kStoreWordPair, R4, R4, 0x40400); 365 366 vixl::aarch32::UseScratchRegisterScope temps(assembler.asm_.GetVIXLAssembler()); 367 temps.Exclude(R12); 368 __ StoreToOffset(kStoreWord, R0, R12, 12); // 32-bit because of R12. 369 temps.Include(R12); 370 __ StoreToOffset(kStoreWord, R2, R4, 0xa4 - 0x100000); 371 372 __ StoreToOffset(kStoreByte, R2, R4, 12); 373 374 EmitAndCheck(&assembler, "VixlStoreToOffset"); 375 } 376 377 #undef __ 378 } // namespace arm 379 } // namespace art 380