Home | History | Annotate | Download | only in utils
      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