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