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 #ifndef ART_COMPILER_UTILS_ASSEMBLER_TEST_H_
18 #define ART_COMPILER_UTILS_ASSEMBLER_TEST_H_
19 
20 #include "assembler.h"
21 
22 #include "assembler_test_base.h"
23 #include "common_runtime_test.h"  // For ScratchFile
24 
25 #include <cstdio>
26 #include <cstdlib>
27 #include <fstream>
28 #include <iterator>
29 #include <sys/stat.h>
30 
31 namespace art {
32 
33 // Helper for a constexpr string length.
34 constexpr size_t ConstexprStrLen(char const* str, size_t count = 0) {
35   return ('\0' == str[0]) ? count : ConstexprStrLen(str+1, count+1);
36 }
37 
38 enum class RegisterView {  // private
39   kUsePrimaryName,
40   kUseSecondaryName,
41   kUseTertiaryName,
42   kUseQuaternaryName,
43 };
44 
45 template<typename Ass, typename Reg, typename FPReg, typename Imm>
46 class AssemblerTest : public testing::Test {
47  public:
GetAssembler()48   Ass* GetAssembler() {
49     return assembler_.get();
50   }
51 
52   typedef std::string (*TestFn)(AssemblerTest* assembler_test, Ass* assembler);
53 
DriverFn(TestFn f,std::string test_name)54   void DriverFn(TestFn f, std::string test_name) {
55     DriverWrapper(f(this, assembler_.get()), test_name);
56   }
57 
58   // This driver assumes the assembler has already been called.
DriverStr(std::string assembly_string,std::string test_name)59   void DriverStr(std::string assembly_string, std::string test_name) {
60     DriverWrapper(assembly_string, test_name);
61   }
62 
RepeatR(void (Ass::* f)(Reg),std::string fmt)63   std::string RepeatR(void (Ass::*f)(Reg), std::string fmt) {
64     return RepeatTemplatedRegister<Reg>(f,
65         GetRegisters(),
66         &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
67         fmt);
68   }
69 
Repeatr(void (Ass::* f)(Reg),std::string fmt)70   std::string Repeatr(void (Ass::*f)(Reg), std::string fmt) {
71     return RepeatTemplatedRegister<Reg>(f,
72         GetRegisters(),
73         &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
74         fmt);
75   }
76 
RepeatRR(void (Ass::* f)(Reg,Reg),std::string fmt)77   std::string RepeatRR(void (Ass::*f)(Reg, Reg), std::string fmt) {
78     return RepeatTemplatedRegisters<Reg, Reg>(f,
79         GetRegisters(),
80         GetRegisters(),
81         &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
82         &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
83         fmt);
84   }
85 
Repeatrr(void (Ass::* f)(Reg,Reg),std::string fmt)86   std::string Repeatrr(void (Ass::*f)(Reg, Reg), std::string fmt) {
87     return RepeatTemplatedRegisters<Reg, Reg>(f,
88         GetRegisters(),
89         GetRegisters(),
90         &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
91         &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
92         fmt);
93   }
94 
Repeatrb(void (Ass::* f)(Reg,Reg),std::string fmt)95   std::string Repeatrb(void (Ass::*f)(Reg, Reg), std::string fmt) {
96     return RepeatTemplatedRegisters<Reg, Reg>(f,
97         GetRegisters(),
98         GetRegisters(),
99         &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
100         &AssemblerTest::GetRegName<RegisterView::kUseQuaternaryName>,
101         fmt);
102   }
103 
RepeatRr(void (Ass::* f)(Reg,Reg),std::string fmt)104   std::string RepeatRr(void (Ass::*f)(Reg, Reg), std::string fmt) {
105     return RepeatTemplatedRegisters<Reg, Reg>(f,
106         GetRegisters(),
107         GetRegisters(),
108         &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
109         &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
110         fmt);
111   }
112 
RepeatRI(void (Ass::* f)(Reg,const Imm &),size_t imm_bytes,std::string fmt)113   std::string RepeatRI(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, std::string fmt) {
114     return RepeatRegisterImm<RegisterView::kUsePrimaryName>(f, imm_bytes, fmt);
115   }
116 
Repeatri(void (Ass::* f)(Reg,const Imm &),size_t imm_bytes,std::string fmt)117   std::string Repeatri(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, std::string fmt) {
118     return RepeatRegisterImm<RegisterView::kUseSecondaryName>(f, imm_bytes, fmt);
119   }
120 
RepeatFF(void (Ass::* f)(FPReg,FPReg),std::string fmt)121   std::string RepeatFF(void (Ass::*f)(FPReg, FPReg), std::string fmt) {
122     return RepeatTemplatedRegisters<FPReg, FPReg>(f,
123                                                   GetFPRegisters(),
124                                                   GetFPRegisters(),
125                                                   &AssemblerTest::GetFPRegName,
126                                                   &AssemblerTest::GetFPRegName,
127                                                   fmt);
128   }
129 
RepeatFFI(void (Ass::* f)(FPReg,FPReg,const Imm &),size_t imm_bytes,std::string fmt)130   std::string RepeatFFI(void (Ass::*f)(FPReg, FPReg, const Imm&), size_t imm_bytes, std::string fmt) {
131     return RepeatTemplatedRegistersImm<FPReg, FPReg>(f,
132                                                   GetFPRegisters(),
133                                                   GetFPRegisters(),
134                                                   &AssemblerTest::GetFPRegName,
135                                                   &AssemblerTest::GetFPRegName,
136                                                   imm_bytes,
137                                                   fmt);
138   }
139 
RepeatFR(void (Ass::* f)(FPReg,Reg),std::string fmt)140   std::string RepeatFR(void (Ass::*f)(FPReg, Reg), std::string fmt) {
141     return RepeatTemplatedRegisters<FPReg, Reg>(f,
142         GetFPRegisters(),
143         GetRegisters(),
144         &AssemblerTest::GetFPRegName,
145         &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
146         fmt);
147   }
148 
RepeatFr(void (Ass::* f)(FPReg,Reg),std::string fmt)149   std::string RepeatFr(void (Ass::*f)(FPReg, Reg), std::string fmt) {
150     return RepeatTemplatedRegisters<FPReg, Reg>(f,
151         GetFPRegisters(),
152         GetRegisters(),
153         &AssemblerTest::GetFPRegName,
154         &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
155         fmt);
156   }
157 
RepeatRF(void (Ass::* f)(Reg,FPReg),std::string fmt)158   std::string RepeatRF(void (Ass::*f)(Reg, FPReg), std::string fmt) {
159     return RepeatTemplatedRegisters<Reg, FPReg>(f,
160         GetRegisters(),
161         GetFPRegisters(),
162         &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
163         &AssemblerTest::GetFPRegName,
164         fmt);
165   }
166 
RepeatrF(void (Ass::* f)(Reg,FPReg),std::string fmt)167   std::string RepeatrF(void (Ass::*f)(Reg, FPReg), std::string fmt) {
168     return RepeatTemplatedRegisters<Reg, FPReg>(f,
169         GetRegisters(),
170         GetFPRegisters(),
171         &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
172         &AssemblerTest::GetFPRegName,
173         fmt);
174   }
175 
176   std::string RepeatI(void (Ass::*f)(const Imm&), size_t imm_bytes, std::string fmt,
177                       bool as_uint = false) {
178     std::string str;
179     std::vector<int64_t> imms = CreateImmediateValues(imm_bytes, as_uint);
180 
181     WarnOnCombinations(imms.size());
182 
183     for (int64_t imm : imms) {
184       Imm new_imm = CreateImmediate(imm);
185       (assembler_.get()->*f)(new_imm);
186       std::string base = fmt;
187 
188       size_t imm_index = base.find(IMM_TOKEN);
189       if (imm_index != std::string::npos) {
190         std::ostringstream sreg;
191         sreg << imm;
192         std::string imm_string = sreg.str();
193         base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
194       }
195 
196       if (str.size() > 0) {
197         str += "\n";
198       }
199       str += base;
200     }
201     // Add a newline at the end.
202     str += "\n";
203     return str;
204   }
205 
206   // This is intended to be run as a test.
CheckTools()207   bool CheckTools() {
208     return test_helper_->CheckTools();
209   }
210 
211   // The following functions are public so that TestFn can use them...
212 
213   virtual std::vector<Reg*> GetRegisters() = 0;
214 
GetFPRegisters()215   virtual std::vector<FPReg*> GetFPRegisters() {
216     UNIMPLEMENTED(FATAL) << "Architecture does not support floating-point registers";
217     UNREACHABLE();
218   }
219 
220   // Secondary register names are the secondary view on registers, e.g., 32b on 64b systems.
GetSecondaryRegisterName(const Reg & reg ATTRIBUTE_UNUSED)221   virtual std::string GetSecondaryRegisterName(const Reg& reg ATTRIBUTE_UNUSED) {
222     UNIMPLEMENTED(FATAL) << "Architecture does not support secondary registers";
223     UNREACHABLE();
224   }
225 
226   // Tertiary register names are the tertiary view on registers, e.g., 16b on 64b systems.
GetTertiaryRegisterName(const Reg & reg ATTRIBUTE_UNUSED)227   virtual std::string GetTertiaryRegisterName(const Reg& reg ATTRIBUTE_UNUSED) {
228     UNIMPLEMENTED(FATAL) << "Architecture does not support tertiary registers";
229     UNREACHABLE();
230   }
231 
232   // Quaternary register names are the quaternary view on registers, e.g., 8b on 64b systems.
GetQuaternaryRegisterName(const Reg & reg ATTRIBUTE_UNUSED)233   virtual std::string GetQuaternaryRegisterName(const Reg& reg ATTRIBUTE_UNUSED) {
234     UNIMPLEMENTED(FATAL) << "Architecture does not support quaternary registers";
235     UNREACHABLE();
236   }
237 
GetRegisterName(const Reg & reg)238   std::string GetRegisterName(const Reg& reg) {
239     return GetRegName<RegisterView::kUsePrimaryName>(reg);
240   }
241 
242  protected:
AssemblerTest()243   explicit AssemblerTest() {}
244 
SetUp()245   void SetUp() OVERRIDE {
246     assembler_.reset(new Ass());
247     test_helper_.reset(
248         new AssemblerTestInfrastructure(GetArchitectureString(),
249                                         GetAssemblerCmdName(),
250                                         GetAssemblerParameters(),
251                                         GetObjdumpCmdName(),
252                                         GetObjdumpParameters(),
253                                         GetDisassembleCmdName(),
254                                         GetDisassembleParameters(),
255                                         GetAssemblyHeader()));
256 
257     SetUpHelpers();
258   }
259 
TearDown()260   void TearDown() OVERRIDE {
261     test_helper_.reset();  // Clean up the helper.
262   }
263 
264   // Override this to set up any architecture-specific things, e.g., register vectors.
SetUpHelpers()265   virtual void SetUpHelpers() {}
266 
267   // Get the typically used name for this architecture, e.g., aarch64, x86_64, ...
268   virtual std::string GetArchitectureString() = 0;
269 
270   // Get the name of the assembler, e.g., "as" by default.
GetAssemblerCmdName()271   virtual std::string GetAssemblerCmdName() {
272     return "as";
273   }
274 
275   // Switches to the assembler command. Default none.
GetAssemblerParameters()276   virtual std::string GetAssemblerParameters() {
277     return "";
278   }
279 
280   // Get the name of the objdump, e.g., "objdump" by default.
GetObjdumpCmdName()281   virtual std::string GetObjdumpCmdName() {
282     return "objdump";
283   }
284 
285   // Switches to the objdump command. Default is " -h".
GetObjdumpParameters()286   virtual std::string GetObjdumpParameters() {
287     return " -h";
288   }
289 
290   // Get the name of the objdump, e.g., "objdump" by default.
GetDisassembleCmdName()291   virtual std::string GetDisassembleCmdName() {
292     return "objdump";
293   }
294 
295   // Switches to the objdump command. As it's a binary, one needs to push the architecture and
296   // such to objdump, so it's architecture-specific and there is no default.
297   virtual std::string GetDisassembleParameters() = 0;
298 
299   // Create a couple of immediate values up to the number of bytes given.
300   virtual std::vector<int64_t> CreateImmediateValues(size_t imm_bytes, bool as_uint = false) {
301     std::vector<int64_t> res;
302     res.push_back(0);
303     if (!as_uint) {
304       res.push_back(-1);
305     } else {
306       res.push_back(0xFF);
307     }
308     res.push_back(0x12);
309     if (imm_bytes >= 2) {
310       res.push_back(0x1234);
311       if (!as_uint) {
312         res.push_back(-0x1234);
313       } else {
314         res.push_back(0xFFFF);
315       }
316       if (imm_bytes >= 4) {
317         res.push_back(0x12345678);
318         if (!as_uint) {
319           res.push_back(-0x12345678);
320         } else {
321           res.push_back(0xFFFFFFFF);
322         }
323         if (imm_bytes >= 6) {
324           res.push_back(0x123456789ABC);
325           if (!as_uint) {
326             res.push_back(-0x123456789ABC);
327           }
328           if (imm_bytes >= 8) {
329             res.push_back(0x123456789ABCDEF0);
330             if (!as_uint) {
331               res.push_back(-0x123456789ABCDEF0);
332             } else {
333               res.push_back(0xFFFFFFFFFFFFFFFF);
334             }
335           }
336         }
337       }
338     }
339     return res;
340   }
341 
342   // Create an immediate from the specific value.
343   virtual Imm CreateImmediate(int64_t imm_value) = 0;
344 
345   template <typename RegType>
RepeatTemplatedRegister(void (Ass::* f)(RegType),const std::vector<RegType * > registers,std::string (AssemblerTest::* GetName)(const RegType &),std::string fmt)346   std::string RepeatTemplatedRegister(void (Ass::*f)(RegType),
347                                       const std::vector<RegType*> registers,
348                                       std::string (AssemblerTest::*GetName)(const RegType&),
349                                       std::string fmt) {
350     std::string str;
351     for (auto reg : registers) {
352       (assembler_.get()->*f)(*reg);
353       std::string base = fmt;
354 
355       std::string reg_string = (this->*GetName)(*reg);
356       size_t reg_index;
357       if ((reg_index = base.find(REG_TOKEN)) != std::string::npos) {
358         base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string);
359       }
360 
361       if (str.size() > 0) {
362         str += "\n";
363       }
364       str += base;
365     }
366     // Add a newline at the end.
367     str += "\n";
368     return str;
369   }
370 
371   template <typename Reg1, typename Reg2>
RepeatTemplatedRegisters(void (Ass::* f)(Reg1,Reg2),const std::vector<Reg1 * > reg1_registers,const std::vector<Reg2 * > reg2_registers,std::string (AssemblerTest::* GetName1)(const Reg1 &),std::string (AssemblerTest::* GetName2)(const Reg2 &),std::string fmt)372   std::string RepeatTemplatedRegisters(void (Ass::*f)(Reg1, Reg2),
373                                        const std::vector<Reg1*> reg1_registers,
374                                        const std::vector<Reg2*> reg2_registers,
375                                        std::string (AssemblerTest::*GetName1)(const Reg1&),
376                                        std::string (AssemblerTest::*GetName2)(const Reg2&),
377                                        std::string fmt) {
378     WarnOnCombinations(reg1_registers.size() * reg2_registers.size());
379 
380     std::string str;
381     for (auto reg1 : reg1_registers) {
382       for (auto reg2 : reg2_registers) {
383         (assembler_.get()->*f)(*reg1, *reg2);
384         std::string base = fmt;
385 
386         std::string reg1_string = (this->*GetName1)(*reg1);
387         size_t reg1_index;
388         while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
389           base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
390         }
391 
392         std::string reg2_string = (this->*GetName2)(*reg2);
393         size_t reg2_index;
394         while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
395           base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
396         }
397 
398         if (str.size() > 0) {
399           str += "\n";
400         }
401         str += base;
402       }
403     }
404     // Add a newline at the end.
405     str += "\n";
406     return str;
407   }
408 
409   template <typename Reg1, typename Reg2>
RepeatTemplatedRegistersImm(void (Ass::* f)(Reg1,Reg2,const Imm &),const std::vector<Reg1 * > reg1_registers,const std::vector<Reg2 * > reg2_registers,std::string (AssemblerTest::* GetName1)(const Reg1 &),std::string (AssemblerTest::* GetName2)(const Reg2 &),size_t imm_bytes,std::string fmt)410   std::string RepeatTemplatedRegistersImm(void (Ass::*f)(Reg1, Reg2, const Imm&),
411                                           const std::vector<Reg1*> reg1_registers,
412                                           const std::vector<Reg2*> reg2_registers,
413                                           std::string (AssemblerTest::*GetName1)(const Reg1&),
414                                           std::string (AssemblerTest::*GetName2)(const Reg2&),
415                                           size_t imm_bytes,
416                                           std::string fmt) {
417     std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
418     WarnOnCombinations(reg1_registers.size() * reg2_registers.size() * imms.size());
419 
420     std::string str;
421     for (auto reg1 : reg1_registers) {
422       for (auto reg2 : reg2_registers) {
423         for (int64_t imm : imms) {
424           Imm new_imm = CreateImmediate(imm);
425           (assembler_.get()->*f)(*reg1, *reg2, new_imm);
426           std::string base = fmt;
427 
428           std::string reg1_string = (this->*GetName1)(*reg1);
429           size_t reg1_index;
430           while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
431             base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
432           }
433 
434           std::string reg2_string = (this->*GetName2)(*reg2);
435           size_t reg2_index;
436           while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
437             base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
438           }
439 
440           size_t imm_index = base.find(IMM_TOKEN);
441           if (imm_index != std::string::npos) {
442             std::ostringstream sreg;
443             sreg << imm;
444             std::string imm_string = sreg.str();
445             base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
446           }
447 
448           if (str.size() > 0) {
449             str += "\n";
450           }
451           str += base;
452         }
453       }
454     }
455     // Add a newline at the end.
456     str += "\n";
457     return str;
458   }
459 
460   template <RegisterView kRegView>
GetRegName(const Reg & reg)461   std::string GetRegName(const Reg& reg) {
462     std::ostringstream sreg;
463     switch (kRegView) {
464       case RegisterView::kUsePrimaryName:
465         sreg << reg;
466         break;
467 
468       case RegisterView::kUseSecondaryName:
469         sreg << GetSecondaryRegisterName(reg);
470         break;
471 
472       case RegisterView::kUseTertiaryName:
473         sreg << GetTertiaryRegisterName(reg);
474         break;
475 
476       case RegisterView::kUseQuaternaryName:
477         sreg << GetQuaternaryRegisterName(reg);
478         break;
479     }
480     return sreg.str();
481   }
482 
GetFPRegName(const FPReg & reg)483   std::string GetFPRegName(const FPReg& reg) {
484     std::ostringstream sreg;
485     sreg << reg;
486     return sreg.str();
487   }
488 
489   // If the assembly file needs a header, return it in a sub-class.
GetAssemblyHeader()490   virtual const char* GetAssemblyHeader() {
491     return nullptr;
492   }
493 
WarnOnCombinations(size_t count)494   void WarnOnCombinations(size_t count) {
495     if (count > kWarnManyCombinationsThreshold) {
496       GTEST_LOG_(WARNING) << "Many combinations (" << count << "), test generation might be slow.";
497     }
498   }
499 
500   static constexpr const char* REG_TOKEN = "{reg}";
501   static constexpr const char* REG1_TOKEN = "{reg1}";
502   static constexpr const char* REG2_TOKEN = "{reg2}";
503   static constexpr const char* IMM_TOKEN = "{imm}";
504 
505  private:
506   template <RegisterView kRegView>
RepeatRegisterImm(void (Ass::* f)(Reg,const Imm &),size_t imm_bytes,std::string fmt)507   std::string RepeatRegisterImm(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes,
508                                   std::string fmt) {
509     const std::vector<Reg*> registers = GetRegisters();
510     std::string str;
511     std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
512 
513     WarnOnCombinations(registers.size() * imms.size());
514 
515     for (auto reg : registers) {
516       for (int64_t imm : imms) {
517         Imm new_imm = CreateImmediate(imm);
518         (assembler_.get()->*f)(*reg, new_imm);
519         std::string base = fmt;
520 
521         std::string reg_string = GetRegName<kRegView>(*reg);
522         size_t reg_index;
523         while ((reg_index = base.find(REG_TOKEN)) != std::string::npos) {
524           base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string);
525         }
526 
527         size_t imm_index = base.find(IMM_TOKEN);
528         if (imm_index != std::string::npos) {
529           std::ostringstream sreg;
530           sreg << imm;
531           std::string imm_string = sreg.str();
532           base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
533         }
534 
535         if (str.size() > 0) {
536           str += "\n";
537         }
538         str += base;
539       }
540     }
541     // Add a newline at the end.
542     str += "\n";
543     return str;
544   }
545 
DriverWrapper(std::string assembly_text,std::string test_name)546   void DriverWrapper(std::string assembly_text, std::string test_name) {
547     size_t cs = assembler_->CodeSize();
548     std::unique_ptr<std::vector<uint8_t>> data(new std::vector<uint8_t>(cs));
549     MemoryRegion code(&(*data)[0], data->size());
550     assembler_->FinalizeInstructions(code);
551     test_helper_->Driver(*data, assembly_text, test_name);
552   }
553 
554   static constexpr size_t kWarnManyCombinationsThreshold = 500;
555 
556   std::unique_ptr<Ass> assembler_;
557   std::unique_ptr<AssemblerTestInfrastructure> test_helper_;
558 
559   DISALLOW_COPY_AND_ASSIGN(AssemblerTest);
560 };
561 
562 }  // namespace art
563 
564 #endif  // ART_COMPILER_UTILS_ASSEMBLER_TEST_H_
565