• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SetAndroidData()51 void SetAndroidData() {
52   const char* data = getenv("ANDROID_DATA");
53   if (data == nullptr) {
54     setenv("ANDROID_DATA", "/tmp", 1);
55   }
56 }
57 
CompareIgnoringSpace(const char * s1,const char * s2)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 
InitResults()71 void InitResults() {
72   if (test_results.empty()) {
73     setup_results();
74   }
75 }
76 
GetToolsDir()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 
DumpAndCheck(std::vector<uint8_t> & code,const char * testname,const char * const * results)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:
ArmVIXLAssemblerTest()180   ArmVIXLAssemblerTest() : pool(), arena(&pool), assembler(&arena) { }
181 
182   ArenaPool pool;
183   ArenaAllocator arena;
184   ArmVIXLJNIMacroAssembler assembler;
185 };
186 
187 #define __ assembler->
188 
EmitAndCheck(ArmVIXLJNIMacroAssembler * assembler,const char * testname,const char * const * results)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 
EmitAndCheck(ArmVIXLJNIMacroAssembler * assembler,const char * testname)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 
TEST_F(ArmVIXLAssemblerTest,VixlJniHelpers)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 
TEST_F(ArmVIXLAssemblerTest,VixlLoadFromOffset)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 
TEST_F(ArmVIXLAssemblerTest,VixlStoreToOffset)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