| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 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 |  | 
| Andreas Gampe | b40c6a7 | 2014-05-02 14:25:12 -0700 | [diff] [blame] | 22 | #include "common_runtime_test.h"  // For ScratchFile | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 23 |  | 
 | 24 | #include <cstdio> | 
 | 25 | #include <cstdlib> | 
 | 26 | #include <fstream> | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 27 | #include <iterator> | 
 | 28 | #include <sys/stat.h> | 
 | 29 |  | 
 | 30 | namespace art { | 
 | 31 |  | 
| Andreas Gampe | 65bec69 | 2015-01-14 12:03:36 -0800 | [diff] [blame] | 32 | // If you want to take a look at the differences between the ART assembler and GCC, set this flag | 
 | 33 | // to true. The disassembled files will then remain in the tmp directory. | 
 | 34 | static constexpr bool kKeepDisassembledFiles = false; | 
 | 35 |  | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 36 | // Helper for a constexpr string length. | 
 | 37 | constexpr size_t ConstexprStrLen(char const* str, size_t count = 0) { | 
 | 38 |   return ('\0' == str[0]) ? count : ConstexprStrLen(str+1, count+1); | 
 | 39 | } | 
 | 40 |  | 
| Andreas Gampe | b40c6a7 | 2014-05-02 14:25:12 -0700 | [diff] [blame] | 41 | // Use a glocal static variable to keep the same name for all test data. Else we'll just spam the | 
 | 42 | // temp directory. | 
 | 43 | static std::string tmpnam_; | 
 | 44 |  | 
| Andreas Gampe | 849cc5e | 2014-11-18 13:46:46 -0800 | [diff] [blame] | 45 | enum class RegisterView {  // private | 
 | 46 |   kUsePrimaryName, | 
 | 47 |   kUseSecondaryName | 
 | 48 | }; | 
 | 49 |  | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 50 | template<typename Ass, typename Reg, typename FPReg, typename Imm> | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 51 | class AssemblerTest : public testing::Test { | 
 | 52 |  public: | 
 | 53 |   Ass* GetAssembler() { | 
 | 54 |     return assembler_.get(); | 
 | 55 |   } | 
 | 56 |  | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 57 |   typedef std::string (*TestFn)(AssemblerTest* assembler_test, Ass* assembler); | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 58 |  | 
 | 59 |   void DriverFn(TestFn f, std::string test_name) { | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 60 |     Driver(f(this, assembler_.get()), test_name); | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 61 |   } | 
 | 62 |  | 
 | 63 |   // This driver assumes the assembler has already been called. | 
 | 64 |   void DriverStr(std::string assembly_string, std::string test_name) { | 
 | 65 |     Driver(assembly_string, test_name); | 
 | 66 |   } | 
 | 67 |  | 
 | 68 |   std::string RepeatR(void (Ass::*f)(Reg), std::string fmt) { | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 69 |     return RepeatTemplatedRegister<Reg>(f, | 
 | 70 |         GetRegisters(), | 
 | 71 |         &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | 
 | 72 |         fmt); | 
 | 73 |   } | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 74 |  | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 75 |   std::string Repeatr(void (Ass::*f)(Reg), std::string fmt) { | 
 | 76 |     return RepeatTemplatedRegister<Reg>(f, | 
 | 77 |         GetRegisters(), | 
 | 78 |         &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, | 
 | 79 |         fmt); | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 80 |   } | 
 | 81 |  | 
 | 82 |   std::string RepeatRR(void (Ass::*f)(Reg, Reg), std::string fmt) { | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 83 |     return RepeatTemplatedRegisters<Reg, Reg>(f, | 
 | 84 |         GetRegisters(), | 
 | 85 |         GetRegisters(), | 
 | 86 |         &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | 
 | 87 |         &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | 
 | 88 |         fmt); | 
 | 89 |   } | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 90 |  | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 91 |   std::string Repeatrr(void (Ass::*f)(Reg, Reg), std::string fmt) { | 
 | 92 |     return RepeatTemplatedRegisters<Reg, Reg>(f, | 
 | 93 |         GetRegisters(), | 
 | 94 |         GetRegisters(), | 
 | 95 |         &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, | 
 | 96 |         &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, | 
 | 97 |         fmt); | 
 | 98 |   } | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 99 |  | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 100 |   std::string RepeatRr(void (Ass::*f)(Reg, Reg), std::string fmt) { | 
 | 101 |     return RepeatTemplatedRegisters<Reg, Reg>(f, | 
 | 102 |         GetRegisters(), | 
 | 103 |         GetRegisters(), | 
 | 104 |         &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | 
 | 105 |         &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, | 
 | 106 |         fmt); | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 107 |   } | 
 | 108 |  | 
 | 109 |   std::string RepeatRI(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, std::string fmt) { | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 110 |     return RepeatRegisterImm<RegisterView::kUsePrimaryName>(f, imm_bytes, fmt); | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 111 |   } | 
 | 112 |  | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 113 |   std::string Repeatri(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, std::string fmt) { | 
 | 114 |     return RepeatRegisterImm<RegisterView::kUseSecondaryName>(f, imm_bytes, fmt); | 
 | 115 |   } | 
 | 116 |  | 
 | 117 |   std::string RepeatFF(void (Ass::*f)(FPReg, FPReg), std::string fmt) { | 
 | 118 |     return RepeatTemplatedRegisters<FPReg, FPReg>(f, | 
 | 119 |                                                   GetFPRegisters(), | 
 | 120 |                                                   GetFPRegisters(), | 
 | 121 |                                                   &AssemblerTest::GetFPRegName, | 
 | 122 |                                                   &AssemblerTest::GetFPRegName, | 
 | 123 |                                                   fmt); | 
 | 124 |   } | 
 | 125 |  | 
 | 126 |   std::string RepeatFR(void (Ass::*f)(FPReg, Reg), std::string fmt) { | 
 | 127 |     return RepeatTemplatedRegisters<FPReg, Reg>(f, | 
 | 128 |         GetFPRegisters(), | 
 | 129 |         GetRegisters(), | 
 | 130 |         &AssemblerTest::GetFPRegName, | 
 | 131 |         &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | 
 | 132 |         fmt); | 
 | 133 |   } | 
 | 134 |  | 
 | 135 |   std::string RepeatFr(void (Ass::*f)(FPReg, Reg), std::string fmt) { | 
 | 136 |     return RepeatTemplatedRegisters<FPReg, Reg>(f, | 
 | 137 |         GetFPRegisters(), | 
 | 138 |         GetRegisters(), | 
 | 139 |         &AssemblerTest::GetFPRegName, | 
 | 140 |         &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, | 
 | 141 |         fmt); | 
 | 142 |   } | 
 | 143 |  | 
 | 144 |   std::string RepeatRF(void (Ass::*f)(Reg, FPReg), std::string fmt) { | 
 | 145 |     return RepeatTemplatedRegisters<Reg, FPReg>(f, | 
 | 146 |         GetRegisters(), | 
 | 147 |         GetFPRegisters(), | 
 | 148 |         &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, | 
 | 149 |         &AssemblerTest::GetFPRegName, | 
 | 150 |         fmt); | 
 | 151 |   } | 
 | 152 |  | 
 | 153 |   std::string RepeatrF(void (Ass::*f)(Reg, FPReg), std::string fmt) { | 
 | 154 |     return RepeatTemplatedRegisters<Reg, FPReg>(f, | 
 | 155 |         GetRegisters(), | 
 | 156 |         GetFPRegisters(), | 
 | 157 |         &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, | 
 | 158 |         &AssemblerTest::GetFPRegName, | 
 | 159 |         fmt); | 
 | 160 |   } | 
 | 161 |  | 
 | 162 |   std::string RepeatI(void (Ass::*f)(const Imm&), size_t imm_bytes, std::string fmt, | 
 | 163 |                       bool as_uint = false) { | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 164 |     std::string str; | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 165 |     std::vector<int64_t> imms = CreateImmediateValues(imm_bytes, as_uint); | 
| Andreas Gampe | 849cc5e | 2014-11-18 13:46:46 -0800 | [diff] [blame] | 166 |  | 
 | 167 |     WarnOnCombinations(imms.size()); | 
 | 168 |  | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 169 |     for (int64_t imm : imms) { | 
| Ian Rogers | cf7f191 | 2014-10-22 22:06:39 -0700 | [diff] [blame] | 170 |       Imm new_imm = CreateImmediate(imm); | 
 | 171 |       (assembler_.get()->*f)(new_imm); | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 172 |       std::string base = fmt; | 
 | 173 |  | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 174 |       size_t imm_index = base.find(IMM_TOKEN); | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 175 |       if (imm_index != std::string::npos) { | 
 | 176 |         std::ostringstream sreg; | 
 | 177 |         sreg << imm; | 
 | 178 |         std::string imm_string = sreg.str(); | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 179 |         base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string); | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 180 |       } | 
 | 181 |  | 
 | 182 |       if (str.size() > 0) { | 
 | 183 |         str += "\n"; | 
 | 184 |       } | 
 | 185 |       str += base; | 
 | 186 |     } | 
 | 187 |     // Add a newline at the end. | 
 | 188 |     str += "\n"; | 
 | 189 |     return str; | 
 | 190 |   } | 
 | 191 |  | 
 | 192 |   // This is intended to be run as a test. | 
 | 193 |   bool CheckTools() { | 
| Andreas Gampe | 849cc5e | 2014-11-18 13:46:46 -0800 | [diff] [blame] | 194 |     if (!FileExists(FindTool(GetAssemblerCmdName()))) { | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 195 |       return false; | 
 | 196 |     } | 
 | 197 |     LOG(INFO) << "Chosen assembler command: " << GetAssemblerCommand(); | 
 | 198 |  | 
| Andreas Gampe | 849cc5e | 2014-11-18 13:46:46 -0800 | [diff] [blame] | 199 |     if (!FileExists(FindTool(GetObjdumpCmdName()))) { | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 200 |       return false; | 
 | 201 |     } | 
 | 202 |     LOG(INFO) << "Chosen objdump command: " << GetObjdumpCommand(); | 
 | 203 |  | 
 | 204 |     // Disassembly is optional. | 
 | 205 |     std::string disassembler = GetDisassembleCommand(); | 
 | 206 |     if (disassembler.length() != 0) { | 
| Andreas Gampe | 849cc5e | 2014-11-18 13:46:46 -0800 | [diff] [blame] | 207 |       if (!FileExists(FindTool(GetDisassembleCmdName()))) { | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 208 |         return false; | 
 | 209 |       } | 
 | 210 |       LOG(INFO) << "Chosen disassemble command: " << GetDisassembleCommand(); | 
 | 211 |     } else { | 
 | 212 |       LOG(INFO) << "No disassembler given."; | 
 | 213 |     } | 
 | 214 |  | 
 | 215 |     return true; | 
 | 216 |   } | 
 | 217 |  | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 218 |   // The following functions are public so that TestFn can use them... | 
 | 219 |  | 
 | 220 |   virtual std::vector<Reg*> GetRegisters() = 0; | 
 | 221 |  | 
 | 222 |   virtual std::vector<FPReg*> GetFPRegisters() { | 
 | 223 |     UNIMPLEMENTED(FATAL) << "Architecture does not support floating-point registers"; | 
 | 224 |     UNREACHABLE(); | 
 | 225 |   } | 
 | 226 |  | 
 | 227 |   // Secondary register names are the secondary view on registers, e.g., 32b on 64b systems. | 
 | 228 |   virtual std::string GetSecondaryRegisterName(const Reg& reg ATTRIBUTE_UNUSED) { | 
 | 229 |     UNIMPLEMENTED(FATAL) << "Architecture does not support secondary registers"; | 
 | 230 |     UNREACHABLE(); | 
 | 231 |   } | 
 | 232 |  | 
| Calin Juravle | 9aec02f | 2014-11-18 23:06:35 +0000 | [diff] [blame] | 233 |   std::string GetRegisterName(const Reg& reg) { | 
 | 234 |     return GetRegName<RegisterView::kUsePrimaryName>(reg); | 
 | 235 |   } | 
 | 236 |  | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 237 |  protected: | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 238 |   explicit AssemblerTest() {} | 
 | 239 |  | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 240 |   void SetUp() OVERRIDE { | 
 | 241 |     assembler_.reset(new Ass()); | 
 | 242 |  | 
| Andreas Gampe | b40c6a7 | 2014-05-02 14:25:12 -0700 | [diff] [blame] | 243 |     // Fake a runtime test for ScratchFile | 
| Andreas Gampe | 7747c8d | 2014-08-06 14:53:03 -0700 | [diff] [blame] | 244 |     CommonRuntimeTest::SetUpAndroidData(android_data_); | 
| Andreas Gampe | b40c6a7 | 2014-05-02 14:25:12 -0700 | [diff] [blame] | 245 |  | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 246 |     SetUpHelpers(); | 
 | 247 |   } | 
 | 248 |  | 
| Andreas Gampe | 7747c8d | 2014-08-06 14:53:03 -0700 | [diff] [blame] | 249 |   void TearDown() OVERRIDE { | 
 | 250 |     // We leave temporaries in case this failed so we can debug issues. | 
 | 251 |     CommonRuntimeTest::TearDownAndroidData(android_data_, false); | 
 | 252 |     tmpnam_ = ""; | 
 | 253 |   } | 
 | 254 |  | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 255 |   // Override this to set up any architecture-specific things, e.g., register vectors. | 
 | 256 |   virtual void SetUpHelpers() {} | 
 | 257 |  | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 258 |   // Get the typically used name for this architecture, e.g., aarch64, x86_64, ... | 
 | 259 |   virtual std::string GetArchitectureString() = 0; | 
 | 260 |  | 
 | 261 |   // Get the name of the assembler, e.g., "as" by default. | 
 | 262 |   virtual std::string GetAssemblerCmdName() { | 
 | 263 |     return "as"; | 
 | 264 |   } | 
 | 265 |  | 
 | 266 |   // Switches to the assembler command. Default none. | 
 | 267 |   virtual std::string GetAssemblerParameters() { | 
 | 268 |     return ""; | 
 | 269 |   } | 
 | 270 |  | 
 | 271 |   // Return the host assembler command for this test. | 
 | 272 |   virtual std::string GetAssemblerCommand() { | 
 | 273 |     // Already resolved it once? | 
 | 274 |     if (resolved_assembler_cmd_.length() != 0) { | 
 | 275 |       return resolved_assembler_cmd_; | 
 | 276 |     } | 
 | 277 |  | 
 | 278 |     std::string line = FindTool(GetAssemblerCmdName()); | 
 | 279 |     if (line.length() == 0) { | 
 | 280 |       return line; | 
 | 281 |     } | 
 | 282 |  | 
 | 283 |     resolved_assembler_cmd_ = line + GetAssemblerParameters(); | 
 | 284 |  | 
| Andreas Gampe | 849cc5e | 2014-11-18 13:46:46 -0800 | [diff] [blame] | 285 |     return resolved_assembler_cmd_; | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 286 |   } | 
 | 287 |  | 
 | 288 |   // Get the name of the objdump, e.g., "objdump" by default. | 
 | 289 |   virtual std::string GetObjdumpCmdName() { | 
 | 290 |     return "objdump"; | 
 | 291 |   } | 
 | 292 |  | 
 | 293 |   // Switches to the objdump command. Default is " -h". | 
 | 294 |   virtual std::string GetObjdumpParameters() { | 
 | 295 |     return " -h"; | 
 | 296 |   } | 
 | 297 |  | 
 | 298 |   // Return the host objdump command for this test. | 
 | 299 |   virtual std::string GetObjdumpCommand() { | 
 | 300 |     // Already resolved it once? | 
 | 301 |     if (resolved_objdump_cmd_.length() != 0) { | 
 | 302 |       return resolved_objdump_cmd_; | 
 | 303 |     } | 
 | 304 |  | 
 | 305 |     std::string line = FindTool(GetObjdumpCmdName()); | 
 | 306 |     if (line.length() == 0) { | 
 | 307 |       return line; | 
 | 308 |     } | 
 | 309 |  | 
 | 310 |     resolved_objdump_cmd_ = line + GetObjdumpParameters(); | 
 | 311 |  | 
| Andreas Gampe | 849cc5e | 2014-11-18 13:46:46 -0800 | [diff] [blame] | 312 |     return resolved_objdump_cmd_; | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 313 |   } | 
 | 314 |  | 
 | 315 |   // Get the name of the objdump, e.g., "objdump" by default. | 
 | 316 |   virtual std::string GetDisassembleCmdName() { | 
 | 317 |     return "objdump"; | 
 | 318 |   } | 
 | 319 |  | 
 | 320 |   // Switches to the objdump command. As it's a binary, one needs to push the architecture and | 
 | 321 |   // such to objdump, so it's architecture-specific and there is no default. | 
 | 322 |   virtual std::string GetDisassembleParameters() = 0; | 
 | 323 |  | 
 | 324 |   // Return the host disassembler command for this test. | 
 | 325 |   virtual std::string GetDisassembleCommand() { | 
 | 326 |     // Already resolved it once? | 
 | 327 |     if (resolved_disassemble_cmd_.length() != 0) { | 
 | 328 |       return resolved_disassemble_cmd_; | 
 | 329 |     } | 
 | 330 |  | 
 | 331 |     std::string line = FindTool(GetDisassembleCmdName()); | 
 | 332 |     if (line.length() == 0) { | 
 | 333 |       return line; | 
 | 334 |     } | 
 | 335 |  | 
 | 336 |     resolved_disassemble_cmd_ = line + GetDisassembleParameters(); | 
 | 337 |  | 
| Andreas Gampe | 849cc5e | 2014-11-18 13:46:46 -0800 | [diff] [blame] | 338 |     return resolved_disassemble_cmd_; | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 339 |   } | 
 | 340 |  | 
 | 341 |   // Create a couple of immediate values up to the number of bytes given. | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 342 |   virtual std::vector<int64_t> CreateImmediateValues(size_t imm_bytes, bool as_uint = false) { | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 343 |     std::vector<int64_t> res; | 
 | 344 |     res.push_back(0); | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 345 |     if (!as_uint) { | 
 | 346 |       res.push_back(-1); | 
 | 347 |     } else { | 
 | 348 |       res.push_back(0xFF); | 
 | 349 |     } | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 350 |     res.push_back(0x12); | 
 | 351 |     if (imm_bytes >= 2) { | 
 | 352 |       res.push_back(0x1234); | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 353 |       if (!as_uint) { | 
 | 354 |         res.push_back(-0x1234); | 
 | 355 |       } else { | 
 | 356 |         res.push_back(0xFFFF); | 
 | 357 |       } | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 358 |       if (imm_bytes >= 4) { | 
 | 359 |         res.push_back(0x12345678); | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 360 |         if (!as_uint) { | 
 | 361 |           res.push_back(-0x12345678); | 
 | 362 |         } else { | 
 | 363 |           res.push_back(0xFFFFFFFF); | 
 | 364 |         } | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 365 |         if (imm_bytes >= 6) { | 
 | 366 |           res.push_back(0x123456789ABC); | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 367 |           if (!as_uint) { | 
 | 368 |             res.push_back(-0x123456789ABC); | 
 | 369 |           } | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 370 |           if (imm_bytes >= 8) { | 
 | 371 |             res.push_back(0x123456789ABCDEF0); | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 372 |             if (!as_uint) { | 
 | 373 |               res.push_back(-0x123456789ABCDEF0); | 
 | 374 |             } else { | 
 | 375 |               res.push_back(0xFFFFFFFFFFFFFFFF); | 
 | 376 |             } | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 377 |           } | 
 | 378 |         } | 
 | 379 |       } | 
 | 380 |     } | 
 | 381 |     return res; | 
 | 382 |   } | 
 | 383 |  | 
 | 384 |   // Create an immediate from the specific value. | 
| Ian Rogers | cf7f191 | 2014-10-22 22:06:39 -0700 | [diff] [blame] | 385 |   virtual Imm CreateImmediate(int64_t imm_value) = 0; | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 386 |  | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 387 |   template <typename RegType> | 
 | 388 |   std::string RepeatTemplatedRegister(void (Ass::*f)(RegType), | 
 | 389 |                                       const std::vector<RegType*> registers, | 
 | 390 |                                       std::string (AssemblerTest::*GetName)(const RegType&), | 
 | 391 |                                       std::string fmt) { | 
 | 392 |     std::string str; | 
 | 393 |     for (auto reg : registers) { | 
 | 394 |       (assembler_.get()->*f)(*reg); | 
 | 395 |       std::string base = fmt; | 
 | 396 |  | 
 | 397 |       std::string reg_string = (this->*GetName)(*reg); | 
 | 398 |       size_t reg_index; | 
 | 399 |       if ((reg_index = base.find(REG_TOKEN)) != std::string::npos) { | 
 | 400 |         base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string); | 
 | 401 |       } | 
 | 402 |  | 
 | 403 |       if (str.size() > 0) { | 
 | 404 |         str += "\n"; | 
 | 405 |       } | 
 | 406 |       str += base; | 
 | 407 |     } | 
 | 408 |     // Add a newline at the end. | 
 | 409 |     str += "\n"; | 
 | 410 |     return str; | 
 | 411 |   } | 
 | 412 |  | 
 | 413 |   template <typename Reg1, typename Reg2> | 
 | 414 |   std::string RepeatTemplatedRegisters(void (Ass::*f)(Reg1, Reg2), | 
 | 415 |                                        const std::vector<Reg1*> reg1_registers, | 
 | 416 |                                        const std::vector<Reg2*> reg2_registers, | 
 | 417 |                                        std::string (AssemblerTest::*GetName1)(const Reg1&), | 
 | 418 |                                        std::string (AssemblerTest::*GetName2)(const Reg2&), | 
 | 419 |                                        std::string fmt) { | 
| Andreas Gampe | 849cc5e | 2014-11-18 13:46:46 -0800 | [diff] [blame] | 420 |     WarnOnCombinations(reg1_registers.size() * reg2_registers.size()); | 
 | 421 |  | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 422 |     std::string str; | 
 | 423 |     for (auto reg1 : reg1_registers) { | 
 | 424 |       for (auto reg2 : reg2_registers) { | 
 | 425 |         (assembler_.get()->*f)(*reg1, *reg2); | 
 | 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 |         if (str.size() > 0) { | 
 | 441 |           str += "\n"; | 
 | 442 |         } | 
 | 443 |         str += base; | 
 | 444 |       } | 
 | 445 |     } | 
 | 446 |     // Add a newline at the end. | 
 | 447 |     str += "\n"; | 
 | 448 |     return str; | 
 | 449 |   } | 
 | 450 |  | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 451 |   template <RegisterView kRegView> | 
 | 452 |   std::string GetRegName(const Reg& reg) { | 
 | 453 |     std::ostringstream sreg; | 
 | 454 |     switch (kRegView) { | 
 | 455 |       case RegisterView::kUsePrimaryName: | 
 | 456 |         sreg << reg; | 
 | 457 |         break; | 
 | 458 |  | 
 | 459 |       case RegisterView::kUseSecondaryName: | 
 | 460 |         sreg << GetSecondaryRegisterName(reg); | 
 | 461 |         break; | 
 | 462 |     } | 
 | 463 |     return sreg.str(); | 
 | 464 |   } | 
 | 465 |  | 
 | 466 |   std::string GetFPRegName(const FPReg& reg) { | 
 | 467 |     std::ostringstream sreg; | 
 | 468 |     sreg << reg; | 
 | 469 |     return sreg.str(); | 
 | 470 |   } | 
 | 471 |  | 
| Andreas Gampe | 849cc5e | 2014-11-18 13:46:46 -0800 | [diff] [blame] | 472 |   // If the assembly file needs a header, return it in a sub-class. | 
 | 473 |   virtual const char* GetAssemblyHeader() { | 
 | 474 |     return nullptr; | 
 | 475 |   } | 
 | 476 |  | 
 | 477 |   void WarnOnCombinations(size_t count) { | 
 | 478 |     if (count > kWarnManyCombinationsThreshold) { | 
 | 479 |       GTEST_LOG_(WARNING) << "Many combinations (" << count << "), test generation might be slow."; | 
 | 480 |     } | 
 | 481 |   } | 
 | 482 |  | 
 | 483 |   static constexpr const char* REG_TOKEN = "{reg}"; | 
 | 484 |   static constexpr const char* REG1_TOKEN = "{reg1}"; | 
 | 485 |   static constexpr const char* REG2_TOKEN = "{reg2}"; | 
 | 486 |   static constexpr const char* IMM_TOKEN = "{imm}"; | 
 | 487 |  | 
 | 488 |  private: | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 489 |   template <RegisterView kRegView> | 
 | 490 |   std::string RepeatRegisterImm(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, | 
 | 491 |                                   std::string fmt) { | 
 | 492 |     const std::vector<Reg*> registers = GetRegisters(); | 
 | 493 |     std::string str; | 
 | 494 |     std::vector<int64_t> imms = CreateImmediateValues(imm_bytes); | 
| Andreas Gampe | 849cc5e | 2014-11-18 13:46:46 -0800 | [diff] [blame] | 495 |  | 
 | 496 |     WarnOnCombinations(registers.size() * imms.size()); | 
 | 497 |  | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 498 |     for (auto reg : registers) { | 
 | 499 |       for (int64_t imm : imms) { | 
 | 500 |         Imm new_imm = CreateImmediate(imm); | 
 | 501 |         (assembler_.get()->*f)(*reg, new_imm); | 
 | 502 |         std::string base = fmt; | 
 | 503 |  | 
 | 504 |         std::string reg_string = GetRegName<kRegView>(*reg); | 
 | 505 |         size_t reg_index; | 
 | 506 |         while ((reg_index = base.find(REG_TOKEN)) != std::string::npos) { | 
 | 507 |           base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string); | 
 | 508 |         } | 
 | 509 |  | 
 | 510 |         size_t imm_index = base.find(IMM_TOKEN); | 
 | 511 |         if (imm_index != std::string::npos) { | 
 | 512 |           std::ostringstream sreg; | 
 | 513 |           sreg << imm; | 
 | 514 |           std::string imm_string = sreg.str(); | 
 | 515 |           base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string); | 
 | 516 |         } | 
 | 517 |  | 
 | 518 |         if (str.size() > 0) { | 
 | 519 |           str += "\n"; | 
 | 520 |         } | 
 | 521 |         str += base; | 
 | 522 |       } | 
 | 523 |     } | 
 | 524 |     // Add a newline at the end. | 
 | 525 |     str += "\n"; | 
 | 526 |     return str; | 
 | 527 |   } | 
 | 528 |  | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 529 |   // Driver() assembles and compares the results. If the results are not equal and we have a | 
 | 530 |   // disassembler, disassemble both and check whether they have the same mnemonics (in which case | 
 | 531 |   // we just warn). | 
 | 532 |   void Driver(std::string assembly_text, std::string test_name) { | 
 | 533 |     EXPECT_NE(assembly_text.length(), 0U) << "Empty assembly"; | 
 | 534 |  | 
 | 535 |     NativeAssemblerResult res; | 
 | 536 |     Compile(assembly_text, &res, test_name); | 
 | 537 |  | 
 | 538 |     EXPECT_TRUE(res.ok) << res.error_msg; | 
 | 539 |     if (!res.ok) { | 
 | 540 |       // No way of continuing. | 
 | 541 |       return; | 
 | 542 |     } | 
 | 543 |  | 
 | 544 |     size_t cs = assembler_->CodeSize(); | 
| Ian Rogers | 700a402 | 2014-05-19 16:49:03 -0700 | [diff] [blame] | 545 |     std::unique_ptr<std::vector<uint8_t>> data(new std::vector<uint8_t>(cs)); | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 546 |     MemoryRegion code(&(*data)[0], data->size()); | 
 | 547 |     assembler_->FinalizeInstructions(code); | 
 | 548 |  | 
 | 549 |     if (*data == *res.code) { | 
 | 550 |       Clean(&res); | 
 | 551 |     } else { | 
 | 552 |       if (DisassembleBinaries(*data, *res.code, test_name)) { | 
 | 553 |         if (data->size() > res.code->size()) { | 
| Andreas Gampe | 54e15de | 2014-08-06 15:31:06 -0700 | [diff] [blame] | 554 |           // Fail this test with a fancy colored warning being printed. | 
 | 555 |           EXPECT_TRUE(false) << "Assembly code is not identical, but disassembly of machine code " | 
 | 556 |               "is equal: this implies sub-optimal encoding! Our code size=" << data->size() << | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 557 |               ", gcc size=" << res.code->size(); | 
 | 558 |         } else { | 
| Andreas Gampe | 54e15de | 2014-08-06 15:31:06 -0700 | [diff] [blame] | 559 |           // Otherwise just print an info message and clean up. | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 560 |           LOG(INFO) << "GCC chose a different encoding than ours, but the overall length is the " | 
 | 561 |               "same."; | 
| Andreas Gampe | 54e15de | 2014-08-06 15:31:06 -0700 | [diff] [blame] | 562 |           Clean(&res); | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 563 |         } | 
 | 564 |       } else { | 
 | 565 |         // This will output the assembly. | 
| Nicolas Geoffray | 102cbed | 2014-10-15 18:31:05 +0100 | [diff] [blame] | 566 |         EXPECT_EQ(*res.code, *data) << "Outputs (and disassembly) not identical."; | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 567 |       } | 
 | 568 |     } | 
 | 569 |   } | 
 | 570 |  | 
 | 571 |   // Structure to store intermediates and results. | 
 | 572 |   struct NativeAssemblerResult { | 
 | 573 |     bool ok; | 
 | 574 |     std::string error_msg; | 
 | 575 |     std::string base_name; | 
| Ian Rogers | 700a402 | 2014-05-19 16:49:03 -0700 | [diff] [blame] | 576 |     std::unique_ptr<std::vector<uint8_t>> code; | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 577 |     uintptr_t length; | 
 | 578 |   }; | 
 | 579 |  | 
 | 580 |   // Compile the assembly file from_file to a binary file to_file. Returns true on success. | 
 | 581 |   bool Assemble(const char* from_file, const char* to_file, std::string* error_msg) { | 
| Andreas Gampe | 849cc5e | 2014-11-18 13:46:46 -0800 | [diff] [blame] | 582 |     bool have_assembler = FileExists(FindTool(GetAssemblerCmdName())); | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 583 |     EXPECT_TRUE(have_assembler) << "Cannot find assembler:" << GetAssemblerCommand(); | 
 | 584 |     if (!have_assembler) { | 
 | 585 |       return false; | 
 | 586 |     } | 
 | 587 |  | 
 | 588 |     std::vector<std::string> args; | 
 | 589 |  | 
| Roland Levillain | 1a28fc4 | 2014-11-13 18:03:06 +0000 | [diff] [blame] | 590 |     // Encaspulate the whole command line in a single string passed to | 
 | 591 |     // the shell, so that GetAssemblerCommand() may contain arguments | 
 | 592 |     // in addition to the program name. | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 593 |     args.push_back(GetAssemblerCommand()); | 
 | 594 |     args.push_back("-o"); | 
 | 595 |     args.push_back(to_file); | 
 | 596 |     args.push_back(from_file); | 
| Roland Levillain | 1a28fc4 | 2014-11-13 18:03:06 +0000 | [diff] [blame] | 597 |     std::string cmd = Join(args, ' '); | 
 | 598 |  | 
 | 599 |     args.clear(); | 
 | 600 |     args.push_back("/bin/sh"); | 
 | 601 |     args.push_back("-c"); | 
 | 602 |     args.push_back(cmd); | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 603 |  | 
| Andreas Gampe | 849cc5e | 2014-11-18 13:46:46 -0800 | [diff] [blame] | 604 |     bool success = Exec(args, error_msg); | 
 | 605 |     if (!success) { | 
 | 606 |       LOG(INFO) << "Assembler command line:"; | 
 | 607 |       for (std::string arg : args) { | 
 | 608 |         LOG(INFO) << arg; | 
 | 609 |       } | 
 | 610 |     } | 
 | 611 |     return success; | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 612 |   } | 
 | 613 |  | 
 | 614 |   // Runs objdump -h on the binary file and extracts the first line with .text. | 
 | 615 |   // Returns "" on failure. | 
 | 616 |   std::string Objdump(std::string file) { | 
| Andreas Gampe | 849cc5e | 2014-11-18 13:46:46 -0800 | [diff] [blame] | 617 |     bool have_objdump = FileExists(FindTool(GetObjdumpCmdName())); | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 618 |     EXPECT_TRUE(have_objdump) << "Cannot find objdump: " << GetObjdumpCommand(); | 
 | 619 |     if (!have_objdump) { | 
 | 620 |       return ""; | 
 | 621 |     } | 
 | 622 |  | 
 | 623 |     std::string error_msg; | 
 | 624 |     std::vector<std::string> args; | 
 | 625 |  | 
| Roland Levillain | 1a28fc4 | 2014-11-13 18:03:06 +0000 | [diff] [blame] | 626 |     // Encaspulate the whole command line in a single string passed to | 
 | 627 |     // the shell, so that GetObjdumpCommand() may contain arguments | 
 | 628 |     // in addition to the program name. | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 629 |     args.push_back(GetObjdumpCommand()); | 
 | 630 |     args.push_back(file); | 
 | 631 |     args.push_back(">"); | 
 | 632 |     args.push_back(file+".dump"); | 
 | 633 |     std::string cmd = Join(args, ' '); | 
 | 634 |  | 
 | 635 |     args.clear(); | 
 | 636 |     args.push_back("/bin/sh"); | 
 | 637 |     args.push_back("-c"); | 
 | 638 |     args.push_back(cmd); | 
 | 639 |  | 
 | 640 |     if (!Exec(args, &error_msg)) { | 
 | 641 |       EXPECT_TRUE(false) << error_msg; | 
 | 642 |     } | 
 | 643 |  | 
 | 644 |     std::ifstream dump(file+".dump"); | 
 | 645 |  | 
 | 646 |     std::string line; | 
 | 647 |     bool found = false; | 
 | 648 |     while (std::getline(dump, line)) { | 
 | 649 |       if (line.find(".text") != line.npos) { | 
 | 650 |         found = true; | 
 | 651 |         break; | 
 | 652 |       } | 
 | 653 |     } | 
 | 654 |  | 
 | 655 |     dump.close(); | 
 | 656 |  | 
 | 657 |     if (found) { | 
 | 658 |       return line; | 
 | 659 |     } else { | 
 | 660 |       return ""; | 
 | 661 |     } | 
 | 662 |   } | 
 | 663 |  | 
 | 664 |   // Disassemble both binaries and compare the text. | 
 | 665 |   bool DisassembleBinaries(std::vector<uint8_t>& data, std::vector<uint8_t>& as, | 
 | 666 |                            std::string test_name) { | 
 | 667 |     std::string disassembler = GetDisassembleCommand(); | 
 | 668 |     if (disassembler.length() == 0) { | 
 | 669 |       LOG(WARNING) << "No dissassembler command."; | 
 | 670 |       return false; | 
 | 671 |     } | 
 | 672 |  | 
 | 673 |     std::string data_name = WriteToFile(data, test_name + ".ass"); | 
 | 674 |     std::string error_msg; | 
 | 675 |     if (!DisassembleBinary(data_name, &error_msg)) { | 
 | 676 |       LOG(INFO) << "Error disassembling: " << error_msg; | 
 | 677 |       std::remove(data_name.c_str()); | 
 | 678 |       return false; | 
 | 679 |     } | 
 | 680 |  | 
 | 681 |     std::string as_name = WriteToFile(as, test_name + ".gcc"); | 
 | 682 |     if (!DisassembleBinary(as_name, &error_msg)) { | 
 | 683 |       LOG(INFO) << "Error disassembling: " << error_msg; | 
 | 684 |       std::remove(data_name.c_str()); | 
 | 685 |       std::remove((data_name + ".dis").c_str()); | 
 | 686 |       std::remove(as_name.c_str()); | 
 | 687 |       return false; | 
 | 688 |     } | 
 | 689 |  | 
 | 690 |     bool result = CompareFiles(data_name + ".dis", as_name + ".dis"); | 
 | 691 |  | 
| Andreas Gampe | 65bec69 | 2015-01-14 12:03:36 -0800 | [diff] [blame] | 692 |     if (!kKeepDisassembledFiles) { | 
 | 693 |       std::remove(data_name.c_str()); | 
 | 694 |       std::remove(as_name.c_str()); | 
 | 695 |       std::remove((data_name + ".dis").c_str()); | 
 | 696 |       std::remove((as_name + ".dis").c_str()); | 
 | 697 |     } | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 698 |  | 
 | 699 |     return result; | 
 | 700 |   } | 
 | 701 |  | 
 | 702 |   bool DisassembleBinary(std::string file, std::string* error_msg) { | 
 | 703 |     std::vector<std::string> args; | 
 | 704 |  | 
| Roland Levillain | 1a28fc4 | 2014-11-13 18:03:06 +0000 | [diff] [blame] | 705 |     // Encaspulate the whole command line in a single string passed to | 
 | 706 |     // the shell, so that GetDisassembleCommand() may contain arguments | 
 | 707 |     // in addition to the program name. | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 708 |     args.push_back(GetDisassembleCommand()); | 
 | 709 |     args.push_back(file); | 
 | 710 |     args.push_back("| sed -n \'/<.data>/,$p\' | sed -e \'s/.*://\'"); | 
 | 711 |     args.push_back(">"); | 
 | 712 |     args.push_back(file+".dis"); | 
 | 713 |     std::string cmd = Join(args, ' '); | 
 | 714 |  | 
 | 715 |     args.clear(); | 
 | 716 |     args.push_back("/bin/sh"); | 
 | 717 |     args.push_back("-c"); | 
 | 718 |     args.push_back(cmd); | 
 | 719 |  | 
 | 720 |     return Exec(args, error_msg); | 
 | 721 |   } | 
 | 722 |  | 
 | 723 |   std::string WriteToFile(std::vector<uint8_t>& buffer, std::string test_name) { | 
 | 724 |     std::string file_name = GetTmpnam() + std::string("---") + test_name; | 
 | 725 |     const char* data = reinterpret_cast<char*>(buffer.data()); | 
 | 726 |     std::ofstream s_out(file_name + ".o"); | 
 | 727 |     s_out.write(data, buffer.size()); | 
 | 728 |     s_out.close(); | 
 | 729 |     return file_name + ".o"; | 
 | 730 |   } | 
 | 731 |  | 
 | 732 |   bool CompareFiles(std::string f1, std::string f2) { | 
 | 733 |     std::ifstream f1_in(f1); | 
 | 734 |     std::ifstream f2_in(f2); | 
 | 735 |  | 
 | 736 |     bool result = std::equal(std::istreambuf_iterator<char>(f1_in), | 
 | 737 |                              std::istreambuf_iterator<char>(), | 
 | 738 |                              std::istreambuf_iterator<char>(f2_in)); | 
 | 739 |  | 
 | 740 |     f1_in.close(); | 
 | 741 |     f2_in.close(); | 
 | 742 |  | 
 | 743 |     return result; | 
 | 744 |   } | 
 | 745 |  | 
 | 746 |   // Compile the given assembly code and extract the binary, if possible. Put result into res. | 
 | 747 |   bool Compile(std::string assembly_code, NativeAssemblerResult* res, std::string test_name) { | 
 | 748 |     res->ok = false; | 
 | 749 |     res->code.reset(nullptr); | 
 | 750 |  | 
 | 751 |     res->base_name = GetTmpnam() + std::string("---") + test_name; | 
 | 752 |  | 
 | 753 |     // TODO: Lots of error checking. | 
 | 754 |  | 
 | 755 |     std::ofstream s_out(res->base_name + ".S"); | 
| Andreas Gampe | 849cc5e | 2014-11-18 13:46:46 -0800 | [diff] [blame] | 756 |     const char* header = GetAssemblyHeader(); | 
 | 757 |     if (header != nullptr) { | 
 | 758 |       s_out << header; | 
 | 759 |     } | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 760 |     s_out << assembly_code; | 
 | 761 |     s_out.close(); | 
 | 762 |  | 
 | 763 |     if (!Assemble((res->base_name + ".S").c_str(), (res->base_name + ".o").c_str(), | 
 | 764 |                   &res->error_msg)) { | 
 | 765 |       res->error_msg = "Could not compile."; | 
 | 766 |       return false; | 
 | 767 |     } | 
 | 768 |  | 
 | 769 |     std::string odump = Objdump(res->base_name + ".o"); | 
 | 770 |     if (odump.length() == 0) { | 
 | 771 |       res->error_msg = "Objdump failed."; | 
 | 772 |       return false; | 
 | 773 |     } | 
 | 774 |  | 
 | 775 |     std::istringstream iss(odump); | 
 | 776 |     std::istream_iterator<std::string> start(iss); | 
 | 777 |     std::istream_iterator<std::string> end; | 
 | 778 |     std::vector<std::string> tokens(start, end); | 
 | 779 |  | 
 | 780 |     if (tokens.size() < OBJDUMP_SECTION_LINE_MIN_TOKENS) { | 
 | 781 |       res->error_msg = "Objdump output not recognized: too few tokens."; | 
 | 782 |       return false; | 
 | 783 |     } | 
 | 784 |  | 
 | 785 |     if (tokens[1] != ".text") { | 
 | 786 |       res->error_msg = "Objdump output not recognized: .text not second token."; | 
 | 787 |       return false; | 
 | 788 |     } | 
 | 789 |  | 
 | 790 |     std::string lengthToken = "0x" + tokens[2]; | 
 | 791 |     std::istringstream(lengthToken) >> std::hex >> res->length; | 
 | 792 |  | 
 | 793 |     std::string offsetToken = "0x" + tokens[5]; | 
 | 794 |     uintptr_t offset; | 
 | 795 |     std::istringstream(offsetToken) >> std::hex >> offset; | 
 | 796 |  | 
 | 797 |     std::ifstream obj(res->base_name + ".o"); | 
 | 798 |     obj.seekg(offset); | 
 | 799 |     res->code.reset(new std::vector<uint8_t>(res->length)); | 
 | 800 |     obj.read(reinterpret_cast<char*>(&(*res->code)[0]), res->length); | 
 | 801 |     obj.close(); | 
 | 802 |  | 
 | 803 |     res->ok = true; | 
 | 804 |     return true; | 
 | 805 |   } | 
 | 806 |  | 
 | 807 |   // Remove temporary files. | 
 | 808 |   void Clean(const NativeAssemblerResult* res) { | 
 | 809 |     std::remove((res->base_name + ".S").c_str()); | 
 | 810 |     std::remove((res->base_name + ".o").c_str()); | 
 | 811 |     std::remove((res->base_name + ".o.dump").c_str()); | 
 | 812 |   } | 
 | 813 |  | 
 | 814 |   // Check whether file exists. Is used for commands, so strips off any parameters: anything after | 
 | 815 |   // the first space. We skip to the last slash for this, so it should work with directories with | 
 | 816 |   // spaces. | 
 | 817 |   static bool FileExists(std::string file) { | 
 | 818 |     if (file.length() == 0) { | 
 | 819 |       return false; | 
 | 820 |     } | 
 | 821 |  | 
 | 822 |     // Need to strip any options. | 
 | 823 |     size_t last_slash = file.find_last_of('/'); | 
 | 824 |     if (last_slash == std::string::npos) { | 
 | 825 |       // No slash, start looking at the start. | 
 | 826 |       last_slash = 0; | 
 | 827 |     } | 
 | 828 |     size_t space_index = file.find(' ', last_slash); | 
 | 829 |  | 
 | 830 |     if (space_index == std::string::npos) { | 
 | 831 |       std::ifstream infile(file.c_str()); | 
 | 832 |       return infile.good(); | 
 | 833 |     } else { | 
 | 834 |       std::string copy = file.substr(0, space_index - 1); | 
 | 835 |  | 
 | 836 |       struct stat buf; | 
 | 837 |       return stat(copy.c_str(), &buf) == 0; | 
 | 838 |     } | 
 | 839 |   } | 
 | 840 |  | 
 | 841 |   static std::string GetGCCRootPath() { | 
 | 842 |     return "prebuilts/gcc/linux-x86"; | 
 | 843 |   } | 
 | 844 |  | 
 | 845 |   static std::string GetRootPath() { | 
 | 846 |     // 1) Check ANDROID_BUILD_TOP | 
 | 847 |     char* build_top = getenv("ANDROID_BUILD_TOP"); | 
 | 848 |     if (build_top != nullptr) { | 
 | 849 |       return std::string(build_top) + "/"; | 
 | 850 |     } | 
 | 851 |  | 
 | 852 |     // 2) Do cwd | 
 | 853 |     char temp[1024]; | 
 | 854 |     return getcwd(temp, 1024) ? std::string(temp) + "/" : std::string(""); | 
 | 855 |   } | 
 | 856 |  | 
 | 857 |   std::string FindTool(std::string tool_name) { | 
 | 858 |     // Find the current tool. Wild-card pattern is "arch-string*tool-name". | 
 | 859 |     std::string gcc_path = GetRootPath() + GetGCCRootPath(); | 
 | 860 |     std::vector<std::string> args; | 
 | 861 |     args.push_back("find"); | 
 | 862 |     args.push_back(gcc_path); | 
 | 863 |     args.push_back("-name"); | 
 | 864 |     args.push_back(GetArchitectureString() + "*" + tool_name); | 
 | 865 |     args.push_back("|"); | 
 | 866 |     args.push_back("sort"); | 
 | 867 |     args.push_back("|"); | 
 | 868 |     args.push_back("tail"); | 
 | 869 |     args.push_back("-n"); | 
 | 870 |     args.push_back("1"); | 
 | 871 |     std::string tmp_file = GetTmpnam(); | 
 | 872 |     args.push_back(">"); | 
 | 873 |     args.push_back(tmp_file); | 
 | 874 |     std::string sh_args = Join(args, ' '); | 
 | 875 |  | 
 | 876 |     args.clear(); | 
 | 877 |     args.push_back("/bin/sh"); | 
 | 878 |     args.push_back("-c"); | 
 | 879 |     args.push_back(sh_args); | 
 | 880 |  | 
 | 881 |     std::string error_msg; | 
 | 882 |     if (!Exec(args, &error_msg)) { | 
 | 883 |       EXPECT_TRUE(false) << error_msg; | 
 | 884 |       return ""; | 
 | 885 |     } | 
 | 886 |  | 
 | 887 |     std::ifstream in(tmp_file.c_str()); | 
 | 888 |     std::string line; | 
 | 889 |     if (!std::getline(in, line)) { | 
 | 890 |       in.close(); | 
 | 891 |       std::remove(tmp_file.c_str()); | 
 | 892 |       return ""; | 
 | 893 |     } | 
 | 894 |     in.close(); | 
 | 895 |     std::remove(tmp_file.c_str()); | 
 | 896 |     return line; | 
 | 897 |   } | 
 | 898 |  | 
 | 899 |   // Use a consistent tmpnam, so store it. | 
 | 900 |   std::string GetTmpnam() { | 
 | 901 |     if (tmpnam_.length() == 0) { | 
| Andreas Gampe | b40c6a7 | 2014-05-02 14:25:12 -0700 | [diff] [blame] | 902 |       ScratchFile tmp; | 
 | 903 |       tmpnam_ = tmp.GetFilename() + "asm"; | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 904 |     } | 
 | 905 |     return tmpnam_; | 
 | 906 |   } | 
 | 907 |  | 
| Andreas Gampe | 849cc5e | 2014-11-18 13:46:46 -0800 | [diff] [blame] | 908 |   static constexpr size_t kWarnManyCombinationsThreshold = 500; | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 909 |   static constexpr size_t OBJDUMP_SECTION_LINE_MIN_TOKENS = 6; | 
 | 910 |  | 
| Ian Rogers | 700a402 | 2014-05-19 16:49:03 -0700 | [diff] [blame] | 911 |   std::unique_ptr<Ass> assembler_; | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 912 |  | 
 | 913 |   std::string resolved_assembler_cmd_; | 
 | 914 |   std::string resolved_objdump_cmd_; | 
 | 915 |   std::string resolved_disassemble_cmd_; | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 916 |  | 
| Andreas Gampe | 7747c8d | 2014-08-06 14:53:03 -0700 | [diff] [blame] | 917 |   std::string android_data_; | 
 | 918 |  | 
| Andreas Gampe | 851df20 | 2014-11-12 14:05:46 -0800 | [diff] [blame] | 919 |   DISALLOW_COPY_AND_ASSIGN(AssemblerTest); | 
| Andreas Gampe | 5a4fa82 | 2014-03-31 16:50:12 -0700 | [diff] [blame] | 920 | }; | 
 | 921 |  | 
 | 922 | }  // namespace art | 
 | 923 |  | 
 | 924 | #endif  // ART_COMPILER_UTILS_ASSEMBLER_TEST_H_ |