blob: 54c931dc9337f0f8a42744bddf4fb25b847183b0 [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
Calin Juravle9aec02f2014-11-18 23:06:35 +0000226 std::string GetRegisterName(const Reg& reg) {
227 return GetRegName<RegisterView::kUsePrimaryName>(reg);
228 }
229
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700230 protected:
Andreas Gampe851df202014-11-12 14:05:46 -0800231 explicit AssemblerTest() {}
232
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700233 void SetUp() OVERRIDE {
234 assembler_.reset(new Ass());
235
Andreas Gampeb40c6a72014-05-02 14:25:12 -0700236 // Fake a runtime test for ScratchFile
Andreas Gampe7747c8d2014-08-06 14:53:03 -0700237 CommonRuntimeTest::SetUpAndroidData(android_data_);
Andreas Gampeb40c6a72014-05-02 14:25:12 -0700238
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700239 SetUpHelpers();
240 }
241
Andreas Gampe7747c8d2014-08-06 14:53:03 -0700242 void TearDown() OVERRIDE {
243 // We leave temporaries in case this failed so we can debug issues.
244 CommonRuntimeTest::TearDownAndroidData(android_data_, false);
245 tmpnam_ = "";
246 }
247
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700248 // Override this to set up any architecture-specific things, e.g., register vectors.
249 virtual void SetUpHelpers() {}
250
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700251 // Get the typically used name for this architecture, e.g., aarch64, x86_64, ...
252 virtual std::string GetArchitectureString() = 0;
253
254 // Get the name of the assembler, e.g., "as" by default.
255 virtual std::string GetAssemblerCmdName() {
256 return "as";
257 }
258
259 // Switches to the assembler command. Default none.
260 virtual std::string GetAssemblerParameters() {
261 return "";
262 }
263
264 // Return the host assembler command for this test.
265 virtual std::string GetAssemblerCommand() {
266 // Already resolved it once?
267 if (resolved_assembler_cmd_.length() != 0) {
268 return resolved_assembler_cmd_;
269 }
270
271 std::string line = FindTool(GetAssemblerCmdName());
272 if (line.length() == 0) {
273 return line;
274 }
275
276 resolved_assembler_cmd_ = line + GetAssemblerParameters();
277
278 return line;
279 }
280
281 // Get the name of the objdump, e.g., "objdump" by default.
282 virtual std::string GetObjdumpCmdName() {
283 return "objdump";
284 }
285
286 // Switches to the objdump command. Default is " -h".
287 virtual std::string GetObjdumpParameters() {
288 return " -h";
289 }
290
291 // Return the host objdump command for this test.
292 virtual std::string GetObjdumpCommand() {
293 // Already resolved it once?
294 if (resolved_objdump_cmd_.length() != 0) {
295 return resolved_objdump_cmd_;
296 }
297
298 std::string line = FindTool(GetObjdumpCmdName());
299 if (line.length() == 0) {
300 return line;
301 }
302
303 resolved_objdump_cmd_ = line + GetObjdumpParameters();
304
305 return line;
306 }
307
308 // Get the name of the objdump, e.g., "objdump" by default.
309 virtual std::string GetDisassembleCmdName() {
310 return "objdump";
311 }
312
313 // Switches to the objdump command. As it's a binary, one needs to push the architecture and
314 // such to objdump, so it's architecture-specific and there is no default.
315 virtual std::string GetDisassembleParameters() = 0;
316
317 // Return the host disassembler command for this test.
318 virtual std::string GetDisassembleCommand() {
319 // Already resolved it once?
320 if (resolved_disassemble_cmd_.length() != 0) {
321 return resolved_disassemble_cmd_;
322 }
323
324 std::string line = FindTool(GetDisassembleCmdName());
325 if (line.length() == 0) {
326 return line;
327 }
328
329 resolved_disassemble_cmd_ = line + GetDisassembleParameters();
330
331 return line;
332 }
333
334 // Create a couple of immediate values up to the number of bytes given.
Andreas Gampe851df202014-11-12 14:05:46 -0800335 virtual std::vector<int64_t> CreateImmediateValues(size_t imm_bytes, bool as_uint = false) {
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700336 std::vector<int64_t> res;
337 res.push_back(0);
Andreas Gampe851df202014-11-12 14:05:46 -0800338 if (!as_uint) {
339 res.push_back(-1);
340 } else {
341 res.push_back(0xFF);
342 }
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700343 res.push_back(0x12);
344 if (imm_bytes >= 2) {
345 res.push_back(0x1234);
Andreas Gampe851df202014-11-12 14:05:46 -0800346 if (!as_uint) {
347 res.push_back(-0x1234);
348 } else {
349 res.push_back(0xFFFF);
350 }
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700351 if (imm_bytes >= 4) {
352 res.push_back(0x12345678);
Andreas Gampe851df202014-11-12 14:05:46 -0800353 if (!as_uint) {
354 res.push_back(-0x12345678);
355 } else {
356 res.push_back(0xFFFFFFFF);
357 }
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700358 if (imm_bytes >= 6) {
359 res.push_back(0x123456789ABC);
Andreas Gampe851df202014-11-12 14:05:46 -0800360 if (!as_uint) {
361 res.push_back(-0x123456789ABC);
362 }
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700363 if (imm_bytes >= 8) {
364 res.push_back(0x123456789ABCDEF0);
Andreas Gampe851df202014-11-12 14:05:46 -0800365 if (!as_uint) {
366 res.push_back(-0x123456789ABCDEF0);
367 } else {
368 res.push_back(0xFFFFFFFFFFFFFFFF);
369 }
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700370 }
371 }
372 }
373 }
374 return res;
375 }
376
377 // Create an immediate from the specific value.
Ian Rogerscf7f1912014-10-22 22:06:39 -0700378 virtual Imm CreateImmediate(int64_t imm_value) = 0;
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700379
Andreas Gampe851df202014-11-12 14:05:46 -0800380 template <typename RegType>
381 std::string RepeatTemplatedRegister(void (Ass::*f)(RegType),
382 const std::vector<RegType*> registers,
383 std::string (AssemblerTest::*GetName)(const RegType&),
384 std::string fmt) {
385 std::string str;
386 for (auto reg : registers) {
387 (assembler_.get()->*f)(*reg);
388 std::string base = fmt;
389
390 std::string reg_string = (this->*GetName)(*reg);
391 size_t reg_index;
392 if ((reg_index = base.find(REG_TOKEN)) != std::string::npos) {
393 base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string);
394 }
395
396 if (str.size() > 0) {
397 str += "\n";
398 }
399 str += base;
400 }
401 // Add a newline at the end.
402 str += "\n";
403 return str;
404 }
405
406 template <typename Reg1, typename Reg2>
407 std::string RepeatTemplatedRegisters(void (Ass::*f)(Reg1, Reg2),
408 const std::vector<Reg1*> reg1_registers,
409 const std::vector<Reg2*> reg2_registers,
410 std::string (AssemblerTest::*GetName1)(const Reg1&),
411 std::string (AssemblerTest::*GetName2)(const Reg2&),
412 std::string fmt) {
413 std::string str;
414 for (auto reg1 : reg1_registers) {
415 for (auto reg2 : reg2_registers) {
416 (assembler_.get()->*f)(*reg1, *reg2);
417 std::string base = fmt;
418
419 std::string reg1_string = (this->*GetName1)(*reg1);
420 size_t reg1_index;
421 while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
422 base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
423 }
424
425 std::string reg2_string = (this->*GetName2)(*reg2);
426 size_t reg2_index;
427 while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
428 base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
429 }
430
431 if (str.size() > 0) {
432 str += "\n";
433 }
434 str += base;
435 }
436 }
437 // Add a newline at the end.
438 str += "\n";
439 return str;
440 }
441
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700442 private:
Andreas Gampe851df202014-11-12 14:05:46 -0800443 template <RegisterView kRegView>
444 std::string GetRegName(const Reg& reg) {
445 std::ostringstream sreg;
446 switch (kRegView) {
447 case RegisterView::kUsePrimaryName:
448 sreg << reg;
449 break;
450
451 case RegisterView::kUseSecondaryName:
452 sreg << GetSecondaryRegisterName(reg);
453 break;
454 }
455 return sreg.str();
456 }
457
458 std::string GetFPRegName(const FPReg& reg) {
459 std::ostringstream sreg;
460 sreg << reg;
461 return sreg.str();
462 }
463
464 template <RegisterView kRegView>
465 std::string RepeatRegisterImm(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes,
466 std::string fmt) {
467 const std::vector<Reg*> registers = GetRegisters();
468 std::string str;
469 std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
470 for (auto reg : registers) {
471 for (int64_t imm : imms) {
472 Imm new_imm = CreateImmediate(imm);
473 (assembler_.get()->*f)(*reg, new_imm);
474 std::string base = fmt;
475
476 std::string reg_string = GetRegName<kRegView>(*reg);
477 size_t reg_index;
478 while ((reg_index = base.find(REG_TOKEN)) != std::string::npos) {
479 base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string);
480 }
481
482 size_t imm_index = base.find(IMM_TOKEN);
483 if (imm_index != std::string::npos) {
484 std::ostringstream sreg;
485 sreg << imm;
486 std::string imm_string = sreg.str();
487 base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
488 }
489
490 if (str.size() > 0) {
491 str += "\n";
492 }
493 str += base;
494 }
495 }
496 // Add a newline at the end.
497 str += "\n";
498 return str;
499 }
500
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700501 // Driver() assembles and compares the results. If the results are not equal and we have a
502 // disassembler, disassemble both and check whether they have the same mnemonics (in which case
503 // we just warn).
504 void Driver(std::string assembly_text, std::string test_name) {
505 EXPECT_NE(assembly_text.length(), 0U) << "Empty assembly";
506
507 NativeAssemblerResult res;
508 Compile(assembly_text, &res, test_name);
509
510 EXPECT_TRUE(res.ok) << res.error_msg;
511 if (!res.ok) {
512 // No way of continuing.
513 return;
514 }
515
516 size_t cs = assembler_->CodeSize();
Ian Rogers700a4022014-05-19 16:49:03 -0700517 std::unique_ptr<std::vector<uint8_t>> data(new std::vector<uint8_t>(cs));
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700518 MemoryRegion code(&(*data)[0], data->size());
519 assembler_->FinalizeInstructions(code);
520
521 if (*data == *res.code) {
522 Clean(&res);
523 } else {
524 if (DisassembleBinaries(*data, *res.code, test_name)) {
525 if (data->size() > res.code->size()) {
Andreas Gampe54e15de2014-08-06 15:31:06 -0700526 // Fail this test with a fancy colored warning being printed.
527 EXPECT_TRUE(false) << "Assembly code is not identical, but disassembly of machine code "
528 "is equal: this implies sub-optimal encoding! Our code size=" << data->size() <<
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700529 ", gcc size=" << res.code->size();
530 } else {
Andreas Gampe54e15de2014-08-06 15:31:06 -0700531 // Otherwise just print an info message and clean up.
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700532 LOG(INFO) << "GCC chose a different encoding than ours, but the overall length is the "
533 "same.";
Andreas Gampe54e15de2014-08-06 15:31:06 -0700534 Clean(&res);
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700535 }
536 } else {
537 // This will output the assembly.
Nicolas Geoffray102cbed2014-10-15 18:31:05 +0100538 EXPECT_EQ(*res.code, *data) << "Outputs (and disassembly) not identical.";
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700539 }
540 }
541 }
542
543 // Structure to store intermediates and results.
544 struct NativeAssemblerResult {
545 bool ok;
546 std::string error_msg;
547 std::string base_name;
Ian Rogers700a4022014-05-19 16:49:03 -0700548 std::unique_ptr<std::vector<uint8_t>> code;
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700549 uintptr_t length;
550 };
551
552 // Compile the assembly file from_file to a binary file to_file. Returns true on success.
553 bool Assemble(const char* from_file, const char* to_file, std::string* error_msg) {
554 bool have_assembler = FileExists(GetAssemblerCommand());
555 EXPECT_TRUE(have_assembler) << "Cannot find assembler:" << GetAssemblerCommand();
556 if (!have_assembler) {
557 return false;
558 }
559
560 std::vector<std::string> args;
561
Roland Levillain1a28fc42014-11-13 18:03:06 +0000562 // Encaspulate the whole command line in a single string passed to
563 // the shell, so that GetAssemblerCommand() may contain arguments
564 // in addition to the program name.
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700565 args.push_back(GetAssemblerCommand());
566 args.push_back("-o");
567 args.push_back(to_file);
568 args.push_back(from_file);
Roland Levillain1a28fc42014-11-13 18:03:06 +0000569 std::string cmd = Join(args, ' ');
570
571 args.clear();
572 args.push_back("/bin/sh");
573 args.push_back("-c");
574 args.push_back(cmd);
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700575
576 return Exec(args, error_msg);
577 }
578
579 // Runs objdump -h on the binary file and extracts the first line with .text.
580 // Returns "" on failure.
581 std::string Objdump(std::string file) {
582 bool have_objdump = FileExists(GetObjdumpCommand());
583 EXPECT_TRUE(have_objdump) << "Cannot find objdump: " << GetObjdumpCommand();
584 if (!have_objdump) {
585 return "";
586 }
587
588 std::string error_msg;
589 std::vector<std::string> args;
590
Roland Levillain1a28fc42014-11-13 18:03:06 +0000591 // Encaspulate the whole command line in a single string passed to
592 // the shell, so that GetObjdumpCommand() may contain arguments
593 // in addition to the program name.
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700594 args.push_back(GetObjdumpCommand());
595 args.push_back(file);
596 args.push_back(">");
597 args.push_back(file+".dump");
598 std::string cmd = Join(args, ' ');
599
600 args.clear();
601 args.push_back("/bin/sh");
602 args.push_back("-c");
603 args.push_back(cmd);
604
605 if (!Exec(args, &error_msg)) {
606 EXPECT_TRUE(false) << error_msg;
607 }
608
609 std::ifstream dump(file+".dump");
610
611 std::string line;
612 bool found = false;
613 while (std::getline(dump, line)) {
614 if (line.find(".text") != line.npos) {
615 found = true;
616 break;
617 }
618 }
619
620 dump.close();
621
622 if (found) {
623 return line;
624 } else {
625 return "";
626 }
627 }
628
629 // Disassemble both binaries and compare the text.
630 bool DisassembleBinaries(std::vector<uint8_t>& data, std::vector<uint8_t>& as,
631 std::string test_name) {
632 std::string disassembler = GetDisassembleCommand();
633 if (disassembler.length() == 0) {
634 LOG(WARNING) << "No dissassembler command.";
635 return false;
636 }
637
638 std::string data_name = WriteToFile(data, test_name + ".ass");
639 std::string error_msg;
640 if (!DisassembleBinary(data_name, &error_msg)) {
641 LOG(INFO) << "Error disassembling: " << error_msg;
642 std::remove(data_name.c_str());
643 return false;
644 }
645
646 std::string as_name = WriteToFile(as, test_name + ".gcc");
647 if (!DisassembleBinary(as_name, &error_msg)) {
648 LOG(INFO) << "Error disassembling: " << error_msg;
649 std::remove(data_name.c_str());
650 std::remove((data_name + ".dis").c_str());
651 std::remove(as_name.c_str());
652 return false;
653 }
654
655 bool result = CompareFiles(data_name + ".dis", as_name + ".dis");
656
Andreas Gampe851df202014-11-12 14:05:46 -0800657 // If you want to take a look at the differences between the ART assembler and GCC, comment
658 // out the removal code.
659 std::remove(data_name.c_str());
660 std::remove(as_name.c_str());
661 std::remove((data_name + ".dis").c_str());
662 std::remove((as_name + ".dis").c_str());
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700663
664 return result;
665 }
666
667 bool DisassembleBinary(std::string file, std::string* error_msg) {
668 std::vector<std::string> args;
669
Roland Levillain1a28fc42014-11-13 18:03:06 +0000670 // Encaspulate the whole command line in a single string passed to
671 // the shell, so that GetDisassembleCommand() may contain arguments
672 // in addition to the program name.
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700673 args.push_back(GetDisassembleCommand());
674 args.push_back(file);
675 args.push_back("| sed -n \'/<.data>/,$p\' | sed -e \'s/.*://\'");
676 args.push_back(">");
677 args.push_back(file+".dis");
678 std::string cmd = Join(args, ' ');
679
680 args.clear();
681 args.push_back("/bin/sh");
682 args.push_back("-c");
683 args.push_back(cmd);
684
685 return Exec(args, error_msg);
686 }
687
688 std::string WriteToFile(std::vector<uint8_t>& buffer, std::string test_name) {
689 std::string file_name = GetTmpnam() + std::string("---") + test_name;
690 const char* data = reinterpret_cast<char*>(buffer.data());
691 std::ofstream s_out(file_name + ".o");
692 s_out.write(data, buffer.size());
693 s_out.close();
694 return file_name + ".o";
695 }
696
697 bool CompareFiles(std::string f1, std::string f2) {
698 std::ifstream f1_in(f1);
699 std::ifstream f2_in(f2);
700
701 bool result = std::equal(std::istreambuf_iterator<char>(f1_in),
702 std::istreambuf_iterator<char>(),
703 std::istreambuf_iterator<char>(f2_in));
704
705 f1_in.close();
706 f2_in.close();
707
708 return result;
709 }
710
711 // Compile the given assembly code and extract the binary, if possible. Put result into res.
712 bool Compile(std::string assembly_code, NativeAssemblerResult* res, std::string test_name) {
713 res->ok = false;
714 res->code.reset(nullptr);
715
716 res->base_name = GetTmpnam() + std::string("---") + test_name;
717
718 // TODO: Lots of error checking.
719
720 std::ofstream s_out(res->base_name + ".S");
721 s_out << assembly_code;
722 s_out.close();
723
724 if (!Assemble((res->base_name + ".S").c_str(), (res->base_name + ".o").c_str(),
725 &res->error_msg)) {
726 res->error_msg = "Could not compile.";
727 return false;
728 }
729
730 std::string odump = Objdump(res->base_name + ".o");
731 if (odump.length() == 0) {
732 res->error_msg = "Objdump failed.";
733 return false;
734 }
735
736 std::istringstream iss(odump);
737 std::istream_iterator<std::string> start(iss);
738 std::istream_iterator<std::string> end;
739 std::vector<std::string> tokens(start, end);
740
741 if (tokens.size() < OBJDUMP_SECTION_LINE_MIN_TOKENS) {
742 res->error_msg = "Objdump output not recognized: too few tokens.";
743 return false;
744 }
745
746 if (tokens[1] != ".text") {
747 res->error_msg = "Objdump output not recognized: .text not second token.";
748 return false;
749 }
750
751 std::string lengthToken = "0x" + tokens[2];
752 std::istringstream(lengthToken) >> std::hex >> res->length;
753
754 std::string offsetToken = "0x" + tokens[5];
755 uintptr_t offset;
756 std::istringstream(offsetToken) >> std::hex >> offset;
757
758 std::ifstream obj(res->base_name + ".o");
759 obj.seekg(offset);
760 res->code.reset(new std::vector<uint8_t>(res->length));
761 obj.read(reinterpret_cast<char*>(&(*res->code)[0]), res->length);
762 obj.close();
763
764 res->ok = true;
765 return true;
766 }
767
768 // Remove temporary files.
769 void Clean(const NativeAssemblerResult* res) {
770 std::remove((res->base_name + ".S").c_str());
771 std::remove((res->base_name + ".o").c_str());
772 std::remove((res->base_name + ".o.dump").c_str());
773 }
774
775 // Check whether file exists. Is used for commands, so strips off any parameters: anything after
776 // the first space. We skip to the last slash for this, so it should work with directories with
777 // spaces.
778 static bool FileExists(std::string file) {
779 if (file.length() == 0) {
780 return false;
781 }
782
783 // Need to strip any options.
784 size_t last_slash = file.find_last_of('/');
785 if (last_slash == std::string::npos) {
786 // No slash, start looking at the start.
787 last_slash = 0;
788 }
789 size_t space_index = file.find(' ', last_slash);
790
791 if (space_index == std::string::npos) {
792 std::ifstream infile(file.c_str());
793 return infile.good();
794 } else {
795 std::string copy = file.substr(0, space_index - 1);
796
797 struct stat buf;
798 return stat(copy.c_str(), &buf) == 0;
799 }
800 }
801
802 static std::string GetGCCRootPath() {
803 return "prebuilts/gcc/linux-x86";
804 }
805
806 static std::string GetRootPath() {
807 // 1) Check ANDROID_BUILD_TOP
808 char* build_top = getenv("ANDROID_BUILD_TOP");
809 if (build_top != nullptr) {
810 return std::string(build_top) + "/";
811 }
812
813 // 2) Do cwd
814 char temp[1024];
815 return getcwd(temp, 1024) ? std::string(temp) + "/" : std::string("");
816 }
817
818 std::string FindTool(std::string tool_name) {
819 // Find the current tool. Wild-card pattern is "arch-string*tool-name".
820 std::string gcc_path = GetRootPath() + GetGCCRootPath();
821 std::vector<std::string> args;
822 args.push_back("find");
823 args.push_back(gcc_path);
824 args.push_back("-name");
825 args.push_back(GetArchitectureString() + "*" + tool_name);
826 args.push_back("|");
827 args.push_back("sort");
828 args.push_back("|");
829 args.push_back("tail");
830 args.push_back("-n");
831 args.push_back("1");
832 std::string tmp_file = GetTmpnam();
833 args.push_back(">");
834 args.push_back(tmp_file);
835 std::string sh_args = Join(args, ' ');
836
837 args.clear();
838 args.push_back("/bin/sh");
839 args.push_back("-c");
840 args.push_back(sh_args);
841
842 std::string error_msg;
843 if (!Exec(args, &error_msg)) {
844 EXPECT_TRUE(false) << error_msg;
845 return "";
846 }
847
848 std::ifstream in(tmp_file.c_str());
849 std::string line;
850 if (!std::getline(in, line)) {
851 in.close();
852 std::remove(tmp_file.c_str());
853 return "";
854 }
855 in.close();
856 std::remove(tmp_file.c_str());
857 return line;
858 }
859
860 // Use a consistent tmpnam, so store it.
861 std::string GetTmpnam() {
862 if (tmpnam_.length() == 0) {
Andreas Gampeb40c6a72014-05-02 14:25:12 -0700863 ScratchFile tmp;
864 tmpnam_ = tmp.GetFilename() + "asm";
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700865 }
866 return tmpnam_;
867 }
868
Andreas Gampe851df202014-11-12 14:05:46 -0800869 static constexpr size_t OBJDUMP_SECTION_LINE_MIN_TOKENS = 6;
870
871 static constexpr const char* REG_TOKEN = "{reg}";
872 static constexpr const char* REG1_TOKEN = "{reg1}";
873 static constexpr const char* REG2_TOKEN = "{reg2}";
874 static constexpr const char* IMM_TOKEN = "{imm}";
875
Ian Rogers700a4022014-05-19 16:49:03 -0700876 std::unique_ptr<Ass> assembler_;
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700877
878 std::string resolved_assembler_cmd_;
879 std::string resolved_objdump_cmd_;
880 std::string resolved_disassemble_cmd_;
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700881
Andreas Gampe7747c8d2014-08-06 14:53:03 -0700882 std::string android_data_;
883
Andreas Gampe851df202014-11-12 14:05:46 -0800884 DISALLOW_COPY_AND_ASSIGN(AssemblerTest);
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700885};
886
887} // namespace art
888
889#endif // ART_COMPILER_UTILS_ASSEMBLER_TEST_H_