blob: 9457da1c369994fa27712f1c1476e89b1c0b1e03 [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 Gampe03b9ee42015-04-24 21:41:45 -070022#include "assembler_test_base.h"
Andreas Gampeb40c6a72014-05-02 14:25:12 -070023#include "common_runtime_test.h" // For ScratchFile
Andreas Gampe5a4fa822014-03-31 16:50:12 -070024
25#include <cstdio>
26#include <cstdlib>
27#include <fstream>
Andreas Gampe5a4fa822014-03-31 16:50:12 -070028#include <iterator>
29#include <sys/stat.h>
30
31namespace art {
32
Andreas Gampe851df202014-11-12 14:05:46 -080033// Helper for a constexpr string length.
34constexpr size_t ConstexprStrLen(char const* str, size_t count = 0) {
35 return ('\0' == str[0]) ? count : ConstexprStrLen(str+1, count+1);
36}
37
Andreas Gampe849cc5e2014-11-18 13:46:46 -080038enum class RegisterView { // private
39 kUsePrimaryName,
Chao-ying Fud23840d2015-04-07 16:03:04 -070040 kUseSecondaryName,
41 kUseTertiaryName,
42 kUseQuaternaryName,
Andreas Gampe849cc5e2014-11-18 13:46:46 -080043};
44
Andreas Gampe851df202014-11-12 14:05:46 -080045template<typename Ass, typename Reg, typename FPReg, typename Imm>
Andreas Gampe5a4fa822014-03-31 16:50:12 -070046class AssemblerTest : public testing::Test {
47 public:
48 Ass* GetAssembler() {
49 return assembler_.get();
50 }
51
Andreas Gampe851df202014-11-12 14:05:46 -080052 typedef std::string (*TestFn)(AssemblerTest* assembler_test, Ass* assembler);
Andreas Gampe5a4fa822014-03-31 16:50:12 -070053
54 void DriverFn(TestFn f, std::string test_name) {
Andreas Gampe03b9ee42015-04-24 21:41:45 -070055 DriverWrapper(f(this, assembler_.get()), test_name);
Andreas Gampe5a4fa822014-03-31 16:50:12 -070056 }
57
58 // This driver assumes the assembler has already been called.
59 void DriverStr(std::string assembly_string, std::string test_name) {
Andreas Gampe03b9ee42015-04-24 21:41:45 -070060 DriverWrapper(assembly_string, test_name);
Andreas Gampe5a4fa822014-03-31 16:50:12 -070061 }
62
63 std::string RepeatR(void (Ass::*f)(Reg), std::string fmt) {
Andreas Gampe851df202014-11-12 14:05:46 -080064 return RepeatTemplatedRegister<Reg>(f,
65 GetRegisters(),
66 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
67 fmt);
68 }
Andreas Gampe5a4fa822014-03-31 16:50:12 -070069
Andreas Gampe851df202014-11-12 14:05:46 -080070 std::string Repeatr(void (Ass::*f)(Reg), std::string fmt) {
71 return RepeatTemplatedRegister<Reg>(f,
72 GetRegisters(),
73 &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
74 fmt);
Andreas Gampe5a4fa822014-03-31 16:50:12 -070075 }
76
77 std::string RepeatRR(void (Ass::*f)(Reg, Reg), std::string fmt) {
Andreas Gampe851df202014-11-12 14:05:46 -080078 return RepeatTemplatedRegisters<Reg, Reg>(f,
79 GetRegisters(),
80 GetRegisters(),
81 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
82 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
83 fmt);
84 }
Andreas Gampe5a4fa822014-03-31 16:50:12 -070085
Chris Larsen51417632015-10-02 13:24:25 -070086 std::string RepeatRRNoDupes(void (Ass::*f)(Reg, Reg), std::string fmt) {
87 return RepeatTemplatedRegistersNoDupes<Reg, Reg>(f,
88 GetRegisters(),
89 GetRegisters(),
90 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
91 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
92 fmt);
93 }
94
Andreas Gampe851df202014-11-12 14:05:46 -080095 std::string Repeatrr(void (Ass::*f)(Reg, Reg), std::string fmt) {
96 return RepeatTemplatedRegisters<Reg, Reg>(f,
97 GetRegisters(),
98 GetRegisters(),
99 &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
100 &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
101 fmt);
102 }
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700103
Chris Larsendbce0d72015-09-17 13:34:00 -0700104 std::string RepeatRRR(void (Ass::*f)(Reg, Reg, Reg), std::string fmt) {
105 return RepeatTemplatedRegisters<Reg, Reg, Reg>(f,
106 GetRegisters(),
107 GetRegisters(),
108 GetRegisters(),
109 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
110 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
111 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
112 fmt);
113 }
114
Chao-ying Fud23840d2015-04-07 16:03:04 -0700115 std::string Repeatrb(void (Ass::*f)(Reg, Reg), std::string fmt) {
116 return RepeatTemplatedRegisters<Reg, Reg>(f,
117 GetRegisters(),
118 GetRegisters(),
119 &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
120 &AssemblerTest::GetRegName<RegisterView::kUseQuaternaryName>,
121 fmt);
122 }
123
Andreas Gampe851df202014-11-12 14:05:46 -0800124 std::string RepeatRr(void (Ass::*f)(Reg, Reg), std::string fmt) {
125 return RepeatTemplatedRegisters<Reg, Reg>(f,
126 GetRegisters(),
127 GetRegisters(),
128 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
129 &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
130 fmt);
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700131 }
132
133 std::string RepeatRI(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, std::string fmt) {
Andreas Gampe851df202014-11-12 14:05:46 -0800134 return RepeatRegisterImm<RegisterView::kUsePrimaryName>(f, imm_bytes, fmt);
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700135 }
136
Andreas Gampe851df202014-11-12 14:05:46 -0800137 std::string Repeatri(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, std::string fmt) {
138 return RepeatRegisterImm<RegisterView::kUseSecondaryName>(f, imm_bytes, fmt);
139 }
140
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200141 template <typename Reg1, typename Reg2, typename ImmType>
142 std::string RepeatTemplatedRegistersImmBits(void (Ass::*f)(Reg1, Reg2, ImmType),
143 int imm_bits,
144 const std::vector<Reg1*> reg1_registers,
145 const std::vector<Reg2*> reg2_registers,
146 std::string (AssemblerTest::*GetName1)(const Reg1&),
147 std::string (AssemblerTest::*GetName2)(const Reg2&),
148 std::string fmt) {
Chris Larsendbce0d72015-09-17 13:34:00 -0700149 std::string str;
150 std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), imm_bits > 0);
151
152 for (auto reg1 : reg1_registers) {
153 for (auto reg2 : reg2_registers) {
154 for (int64_t imm : imms) {
155 ImmType new_imm = CreateImmediate(imm);
156 (assembler_.get()->*f)(*reg1, *reg2, new_imm);
157 std::string base = fmt;
158
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200159 std::string reg1_string = (this->*GetName1)(*reg1);
Chris Larsendbce0d72015-09-17 13:34:00 -0700160 size_t reg1_index;
161 while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
162 base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
163 }
164
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200165 std::string reg2_string = (this->*GetName2)(*reg2);
Chris Larsendbce0d72015-09-17 13:34:00 -0700166 size_t reg2_index;
167 while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
168 base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
169 }
170
171 size_t imm_index = base.find(IMM_TOKEN);
172 if (imm_index != std::string::npos) {
173 std::ostringstream sreg;
174 sreg << imm;
175 std::string imm_string = sreg.str();
176 base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
177 }
178
179 if (str.size() > 0) {
180 str += "\n";
181 }
182 str += base;
183 }
184 }
185 }
186 // Add a newline at the end.
187 str += "\n";
188 return str;
189 }
190
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200191 template <typename RegType, typename ImmType>
192 std::string RepeatTemplatedRegisterImmBits(void (Ass::*f)(RegType, ImmType),
193 int imm_bits,
194 const std::vector<Reg*> registers,
195 std::string (AssemblerTest::*GetName)(const RegType&),
196 std::string fmt) {
197 std::string str;
198 std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), imm_bits > 0);
199
200 for (auto reg : registers) {
201 for (int64_t imm : imms) {
202 ImmType new_imm = CreateImmediate(imm);
203 (assembler_.get()->*f)(*reg, new_imm);
204 std::string base = fmt;
205
206 std::string reg_string = (this->*GetName)(*reg);
207 size_t reg_index;
208 while ((reg_index = base.find(REG_TOKEN)) != std::string::npos) {
209 base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string);
210 }
211
212 size_t imm_index = base.find(IMM_TOKEN);
213 if (imm_index != std::string::npos) {
214 std::ostringstream sreg;
215 sreg << imm;
216 std::string imm_string = sreg.str();
217 base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
218 }
219
220 if (str.size() > 0) {
221 str += "\n";
222 }
223 str += base;
224 }
225 }
226 // Add a newline at the end.
227 str += "\n";
228 return str;
229 }
230
231 template <typename ImmType>
232 std::string RepeatRRIb(void (Ass::*f)(Reg, Reg, ImmType), int imm_bits, std::string fmt) {
233 return RepeatTemplatedRegistersImmBits<Reg, Reg, ImmType>(f,
234 imm_bits,
235 GetRegisters(),
236 GetRegisters(),
237 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
238 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
239 fmt);
240 }
241
242 template <typename ImmType>
243 std::string RepeatRIb(void (Ass::*f)(Reg, ImmType), int imm_bits, std::string fmt) {
244 return RepeatTemplatedRegisterImmBits<Reg, ImmType>(f,
245 imm_bits,
246 GetRegisters(),
247 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
248 fmt);
249 }
250
251 template <typename ImmType>
252 std::string RepeatFRIb(void (Ass::*f)(FPReg, Reg, ImmType), int imm_bits, std::string fmt) {
253 return RepeatTemplatedRegistersImmBits<FPReg, Reg, ImmType>(f,
254 imm_bits,
255 GetFPRegisters(),
256 GetRegisters(),
257 &AssemblerTest::GetFPRegName,
258 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
259 fmt);
Chris Larsendbce0d72015-09-17 13:34:00 -0700260 }
261
Andreas Gampe851df202014-11-12 14:05:46 -0800262 std::string RepeatFF(void (Ass::*f)(FPReg, FPReg), std::string fmt) {
263 return RepeatTemplatedRegisters<FPReg, FPReg>(f,
264 GetFPRegisters(),
265 GetFPRegisters(),
266 &AssemblerTest::GetFPRegName,
267 &AssemblerTest::GetFPRegName,
268 fmt);
269 }
270
Chris Larsendbce0d72015-09-17 13:34:00 -0700271 std::string RepeatFFF(void (Ass::*f)(FPReg, FPReg, FPReg), std::string fmt) {
272 return RepeatTemplatedRegisters<FPReg, FPReg, FPReg>(f,
273 GetFPRegisters(),
274 GetFPRegisters(),
275 GetFPRegisters(),
276 &AssemblerTest::GetFPRegName,
277 &AssemblerTest::GetFPRegName,
278 &AssemblerTest::GetFPRegName,
279 fmt);
280 }
281
282 std::string RepeatFFI(void (Ass::*f)(FPReg, FPReg, const Imm&),
283 size_t imm_bytes,
284 std::string fmt) {
Mark Mendellfb8d2792015-03-31 22:16:59 -0400285 return RepeatTemplatedRegistersImm<FPReg, FPReg>(f,
Chris Larsendbce0d72015-09-17 13:34:00 -0700286 GetFPRegisters(),
287 GetFPRegisters(),
288 &AssemblerTest::GetFPRegName,
289 &AssemblerTest::GetFPRegName,
290 imm_bytes,
291 fmt);
Mark Mendellfb8d2792015-03-31 22:16:59 -0400292 }
293
Andreas Gampe851df202014-11-12 14:05:46 -0800294 std::string RepeatFR(void (Ass::*f)(FPReg, Reg), std::string fmt) {
295 return RepeatTemplatedRegisters<FPReg, Reg>(f,
296 GetFPRegisters(),
297 GetRegisters(),
298 &AssemblerTest::GetFPRegName,
299 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
300 fmt);
301 }
302
303 std::string RepeatFr(void (Ass::*f)(FPReg, Reg), std::string fmt) {
304 return RepeatTemplatedRegisters<FPReg, Reg>(f,
305 GetFPRegisters(),
306 GetRegisters(),
307 &AssemblerTest::GetFPRegName,
308 &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
309 fmt);
310 }
311
312 std::string RepeatRF(void (Ass::*f)(Reg, FPReg), std::string fmt) {
313 return RepeatTemplatedRegisters<Reg, FPReg>(f,
314 GetRegisters(),
315 GetFPRegisters(),
316 &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
317 &AssemblerTest::GetFPRegName,
318 fmt);
319 }
320
321 std::string RepeatrF(void (Ass::*f)(Reg, FPReg), std::string fmt) {
322 return RepeatTemplatedRegisters<Reg, FPReg>(f,
323 GetRegisters(),
324 GetFPRegisters(),
325 &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
326 &AssemblerTest::GetFPRegName,
327 fmt);
328 }
329
330 std::string RepeatI(void (Ass::*f)(const Imm&), size_t imm_bytes, std::string fmt,
331 bool as_uint = false) {
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700332 std::string str;
Andreas Gampe851df202014-11-12 14:05:46 -0800333 std::vector<int64_t> imms = CreateImmediateValues(imm_bytes, as_uint);
Andreas Gampe849cc5e2014-11-18 13:46:46 -0800334
335 WarnOnCombinations(imms.size());
336
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700337 for (int64_t imm : imms) {
Ian Rogerscf7f1912014-10-22 22:06:39 -0700338 Imm new_imm = CreateImmediate(imm);
339 (assembler_.get()->*f)(new_imm);
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700340 std::string base = fmt;
341
Andreas Gampe851df202014-11-12 14:05:46 -0800342 size_t imm_index = base.find(IMM_TOKEN);
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700343 if (imm_index != std::string::npos) {
344 std::ostringstream sreg;
345 sreg << imm;
346 std::string imm_string = sreg.str();
Andreas Gampe851df202014-11-12 14:05:46 -0800347 base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700348 }
349
350 if (str.size() > 0) {
351 str += "\n";
352 }
353 str += base;
354 }
355 // Add a newline at the end.
356 str += "\n";
357 return str;
358 }
359
360 // This is intended to be run as a test.
361 bool CheckTools() {
Andreas Gampe03b9ee42015-04-24 21:41:45 -0700362 return test_helper_->CheckTools();
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700363 }
364
Andreas Gampe851df202014-11-12 14:05:46 -0800365 // The following functions are public so that TestFn can use them...
366
367 virtual std::vector<Reg*> GetRegisters() = 0;
368
369 virtual std::vector<FPReg*> GetFPRegisters() {
370 UNIMPLEMENTED(FATAL) << "Architecture does not support floating-point registers";
371 UNREACHABLE();
372 }
373
374 // Secondary register names are the secondary view on registers, e.g., 32b on 64b systems.
375 virtual std::string GetSecondaryRegisterName(const Reg& reg ATTRIBUTE_UNUSED) {
376 UNIMPLEMENTED(FATAL) << "Architecture does not support secondary registers";
377 UNREACHABLE();
378 }
379
Chao-ying Fud23840d2015-04-07 16:03:04 -0700380 // Tertiary register names are the tertiary view on registers, e.g., 16b on 64b systems.
381 virtual std::string GetTertiaryRegisterName(const Reg& reg ATTRIBUTE_UNUSED) {
382 UNIMPLEMENTED(FATAL) << "Architecture does not support tertiary registers";
383 UNREACHABLE();
384 }
385
386 // Quaternary register names are the quaternary view on registers, e.g., 8b on 64b systems.
387 virtual std::string GetQuaternaryRegisterName(const Reg& reg ATTRIBUTE_UNUSED) {
388 UNIMPLEMENTED(FATAL) << "Architecture does not support quaternary registers";
389 UNREACHABLE();
390 }
391
Calin Juravle9aec02f2014-11-18 23:06:35 +0000392 std::string GetRegisterName(const Reg& reg) {
393 return GetRegName<RegisterView::kUsePrimaryName>(reg);
394 }
395
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700396 protected:
Andreas Gampe851df202014-11-12 14:05:46 -0800397 explicit AssemblerTest() {}
398
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700399 void SetUp() OVERRIDE {
400 assembler_.reset(new Ass());
Andreas Gampe03b9ee42015-04-24 21:41:45 -0700401 test_helper_.reset(
402 new AssemblerTestInfrastructure(GetArchitectureString(),
403 GetAssemblerCmdName(),
404 GetAssemblerParameters(),
405 GetObjdumpCmdName(),
406 GetObjdumpParameters(),
407 GetDisassembleCmdName(),
408 GetDisassembleParameters(),
409 GetAssemblyHeader()));
Andreas Gampeb40c6a72014-05-02 14:25:12 -0700410
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700411 SetUpHelpers();
412 }
413
Andreas Gampe7747c8d2014-08-06 14:53:03 -0700414 void TearDown() OVERRIDE {
Andreas Gampe03b9ee42015-04-24 21:41:45 -0700415 test_helper_.reset(); // Clean up the helper.
Andreas Gampe7747c8d2014-08-06 14:53:03 -0700416 }
417
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700418 // Override this to set up any architecture-specific things, e.g., register vectors.
419 virtual void SetUpHelpers() {}
420
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700421 // Get the typically used name for this architecture, e.g., aarch64, x86_64, ...
422 virtual std::string GetArchitectureString() = 0;
423
424 // Get the name of the assembler, e.g., "as" by default.
425 virtual std::string GetAssemblerCmdName() {
426 return "as";
427 }
428
429 // Switches to the assembler command. Default none.
430 virtual std::string GetAssemblerParameters() {
431 return "";
432 }
433
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700434 // Get the name of the objdump, e.g., "objdump" by default.
435 virtual std::string GetObjdumpCmdName() {
436 return "objdump";
437 }
438
439 // Switches to the objdump command. Default is " -h".
440 virtual std::string GetObjdumpParameters() {
441 return " -h";
442 }
443
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700444 // Get the name of the objdump, e.g., "objdump" by default.
445 virtual std::string GetDisassembleCmdName() {
446 return "objdump";
447 }
448
449 // Switches to the objdump command. As it's a binary, one needs to push the architecture and
450 // such to objdump, so it's architecture-specific and there is no default.
451 virtual std::string GetDisassembleParameters() = 0;
452
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700453 // Create a couple of immediate values up to the number of bytes given.
Andreas Gampe851df202014-11-12 14:05:46 -0800454 virtual std::vector<int64_t> CreateImmediateValues(size_t imm_bytes, bool as_uint = false) {
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700455 std::vector<int64_t> res;
456 res.push_back(0);
Andreas Gampe851df202014-11-12 14:05:46 -0800457 if (!as_uint) {
458 res.push_back(-1);
459 } else {
460 res.push_back(0xFF);
461 }
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700462 res.push_back(0x12);
463 if (imm_bytes >= 2) {
464 res.push_back(0x1234);
Andreas Gampe851df202014-11-12 14:05:46 -0800465 if (!as_uint) {
466 res.push_back(-0x1234);
467 } else {
468 res.push_back(0xFFFF);
469 }
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700470 if (imm_bytes >= 4) {
471 res.push_back(0x12345678);
Andreas Gampe851df202014-11-12 14:05:46 -0800472 if (!as_uint) {
473 res.push_back(-0x12345678);
474 } else {
475 res.push_back(0xFFFFFFFF);
476 }
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700477 if (imm_bytes >= 6) {
478 res.push_back(0x123456789ABC);
Andreas Gampe851df202014-11-12 14:05:46 -0800479 if (!as_uint) {
480 res.push_back(-0x123456789ABC);
481 }
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700482 if (imm_bytes >= 8) {
483 res.push_back(0x123456789ABCDEF0);
Andreas Gampe851df202014-11-12 14:05:46 -0800484 if (!as_uint) {
485 res.push_back(-0x123456789ABCDEF0);
486 } else {
487 res.push_back(0xFFFFFFFFFFFFFFFF);
488 }
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700489 }
490 }
491 }
492 }
493 return res;
494 }
495
Chris Larsendbce0d72015-09-17 13:34:00 -0700496 const int kMaxBitsExhaustiveTest = 8;
497
498 // Create a couple of immediate values up to the number of bits given.
499 virtual std::vector<int64_t> CreateImmediateValuesBits(const int imm_bits, bool as_uint = false) {
500 CHECK_GT(imm_bits, 0);
501 CHECK_LE(imm_bits, 64);
502 std::vector<int64_t> res;
503
504 if (imm_bits <= kMaxBitsExhaustiveTest) {
505 if (as_uint) {
506 for (uint64_t i = MinInt<uint64_t>(imm_bits); i <= MaxInt<uint64_t>(imm_bits); i++) {
507 res.push_back(static_cast<int64_t>(i));
508 }
509 } else {
510 for (int64_t i = MinInt<int64_t>(imm_bits); i <= MaxInt<int64_t>(imm_bits); i++) {
511 res.push_back(i);
512 }
513 }
514 } else {
515 if (as_uint) {
516 for (uint64_t i = MinInt<uint64_t>(kMaxBitsExhaustiveTest);
517 i <= MaxInt<uint64_t>(kMaxBitsExhaustiveTest);
518 i++) {
519 res.push_back(static_cast<int64_t>(i));
520 }
521 for (int i = 0; i <= imm_bits; i++) {
522 uint64_t j = (MaxInt<uint64_t>(kMaxBitsExhaustiveTest) + 1) +
523 ((MaxInt<uint64_t>(imm_bits) -
524 (MaxInt<uint64_t>(kMaxBitsExhaustiveTest) + 1))
525 * i / imm_bits);
526 res.push_back(static_cast<int64_t>(j));
527 }
528 } else {
529 for (int i = 0; i <= imm_bits; i++) {
530 int64_t j = MinInt<int64_t>(imm_bits) +
531 ((((MinInt<int64_t>(kMaxBitsExhaustiveTest) - 1) -
532 MinInt<int64_t>(imm_bits))
533 * i) / imm_bits);
534 res.push_back(static_cast<int64_t>(j));
535 }
536 for (int64_t i = MinInt<int64_t>(kMaxBitsExhaustiveTest);
537 i <= MaxInt<int64_t>(kMaxBitsExhaustiveTest);
538 i++) {
539 res.push_back(static_cast<int64_t>(i));
540 }
541 for (int i = 0; i <= imm_bits; i++) {
542 int64_t j = (MaxInt<int64_t>(kMaxBitsExhaustiveTest) + 1) +
543 ((MaxInt<int64_t>(imm_bits) - (MaxInt<int64_t>(kMaxBitsExhaustiveTest) + 1))
544 * i / imm_bits);
545 res.push_back(static_cast<int64_t>(j));
546 }
547 }
548 }
549
550 return res;
551 }
552
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700553 // Create an immediate from the specific value.
Ian Rogerscf7f1912014-10-22 22:06:39 -0700554 virtual Imm CreateImmediate(int64_t imm_value) = 0;
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700555
Andreas Gampe851df202014-11-12 14:05:46 -0800556 template <typename RegType>
557 std::string RepeatTemplatedRegister(void (Ass::*f)(RegType),
558 const std::vector<RegType*> registers,
559 std::string (AssemblerTest::*GetName)(const RegType&),
560 std::string fmt) {
561 std::string str;
562 for (auto reg : registers) {
563 (assembler_.get()->*f)(*reg);
564 std::string base = fmt;
565
566 std::string reg_string = (this->*GetName)(*reg);
567 size_t reg_index;
568 if ((reg_index = base.find(REG_TOKEN)) != std::string::npos) {
569 base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string);
570 }
571
572 if (str.size() > 0) {
573 str += "\n";
574 }
575 str += base;
576 }
577 // Add a newline at the end.
578 str += "\n";
579 return str;
580 }
581
582 template <typename Reg1, typename Reg2>
583 std::string RepeatTemplatedRegisters(void (Ass::*f)(Reg1, Reg2),
584 const std::vector<Reg1*> reg1_registers,
585 const std::vector<Reg2*> reg2_registers,
586 std::string (AssemblerTest::*GetName1)(const Reg1&),
587 std::string (AssemblerTest::*GetName2)(const Reg2&),
588 std::string fmt) {
Andreas Gampe849cc5e2014-11-18 13:46:46 -0800589 WarnOnCombinations(reg1_registers.size() * reg2_registers.size());
590
Andreas Gampe851df202014-11-12 14:05:46 -0800591 std::string str;
592 for (auto reg1 : reg1_registers) {
593 for (auto reg2 : reg2_registers) {
594 (assembler_.get()->*f)(*reg1, *reg2);
595 std::string base = fmt;
596
597 std::string reg1_string = (this->*GetName1)(*reg1);
598 size_t reg1_index;
599 while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
600 base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
601 }
602
603 std::string reg2_string = (this->*GetName2)(*reg2);
604 size_t reg2_index;
605 while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
606 base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
607 }
608
609 if (str.size() > 0) {
610 str += "\n";
611 }
612 str += base;
613 }
614 }
615 // Add a newline at the end.
616 str += "\n";
617 return str;
618 }
619
Chris Larsen51417632015-10-02 13:24:25 -0700620 template <typename Reg1, typename Reg2>
621 std::string RepeatTemplatedRegistersNoDupes(void (Ass::*f)(Reg1, Reg2),
622 const std::vector<Reg1*> reg1_registers,
623 const std::vector<Reg2*> reg2_registers,
624 std::string (AssemblerTest::*GetName1)(const Reg1&),
625 std::string (AssemblerTest::*GetName2)(const Reg2&),
626 std::string fmt) {
627 WarnOnCombinations(reg1_registers.size() * reg2_registers.size());
628
629 std::string str;
630 for (auto reg1 : reg1_registers) {
631 for (auto reg2 : reg2_registers) {
632 if (reg1 == reg2) continue;
633 (assembler_.get()->*f)(*reg1, *reg2);
634 std::string base = fmt;
635
636 std::string reg1_string = (this->*GetName1)(*reg1);
637 size_t reg1_index;
638 while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
639 base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
640 }
641
642 std::string reg2_string = (this->*GetName2)(*reg2);
643 size_t reg2_index;
644 while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
645 base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
646 }
647
648 if (str.size() > 0) {
649 str += "\n";
650 }
651 str += base;
652 }
653 }
654 // Add a newline at the end.
655 str += "\n";
656 return str;
657 }
658
Chris Larsendbce0d72015-09-17 13:34:00 -0700659 template <typename Reg1, typename Reg2, typename Reg3>
660 std::string RepeatTemplatedRegisters(void (Ass::*f)(Reg1, Reg2, Reg3),
661 const std::vector<Reg1*> reg1_registers,
662 const std::vector<Reg2*> reg2_registers,
663 const std::vector<Reg3*> reg3_registers,
664 std::string (AssemblerTest::*GetName1)(const Reg1&),
665 std::string (AssemblerTest::*GetName2)(const Reg2&),
666 std::string (AssemblerTest::*GetName3)(const Reg3&),
667 std::string fmt) {
668 std::string str;
669 for (auto reg1 : reg1_registers) {
670 for (auto reg2 : reg2_registers) {
671 for (auto reg3 : reg3_registers) {
672 (assembler_.get()->*f)(*reg1, *reg2, *reg3);
673 std::string base = fmt;
674
675 std::string reg1_string = (this->*GetName1)(*reg1);
676 size_t reg1_index;
677 while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
678 base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
679 }
680
681 std::string reg2_string = (this->*GetName2)(*reg2);
682 size_t reg2_index;
683 while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
684 base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
685 }
686
687 std::string reg3_string = (this->*GetName3)(*reg3);
688 size_t reg3_index;
689 while ((reg3_index = base.find(REG3_TOKEN)) != std::string::npos) {
690 base.replace(reg3_index, ConstexprStrLen(REG3_TOKEN), reg3_string);
691 }
692
693 if (str.size() > 0) {
694 str += "\n";
695 }
696 str += base;
697 }
698 }
699 }
700 // Add a newline at the end.
701 str += "\n";
702 return str;
703 }
704
Mark Mendellfb8d2792015-03-31 22:16:59 -0400705 template <typename Reg1, typename Reg2>
706 std::string RepeatTemplatedRegistersImm(void (Ass::*f)(Reg1, Reg2, const Imm&),
707 const std::vector<Reg1*> reg1_registers,
708 const std::vector<Reg2*> reg2_registers,
709 std::string (AssemblerTest::*GetName1)(const Reg1&),
710 std::string (AssemblerTest::*GetName2)(const Reg2&),
711 size_t imm_bytes,
712 std::string fmt) {
713 std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
714 WarnOnCombinations(reg1_registers.size() * reg2_registers.size() * imms.size());
715
716 std::string str;
717 for (auto reg1 : reg1_registers) {
718 for (auto reg2 : reg2_registers) {
719 for (int64_t imm : imms) {
720 Imm new_imm = CreateImmediate(imm);
721 (assembler_.get()->*f)(*reg1, *reg2, new_imm);
722 std::string base = fmt;
723
724 std::string reg1_string = (this->*GetName1)(*reg1);
725 size_t reg1_index;
726 while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
727 base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
728 }
729
730 std::string reg2_string = (this->*GetName2)(*reg2);
731 size_t reg2_index;
732 while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
733 base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
734 }
735
736 size_t imm_index = base.find(IMM_TOKEN);
737 if (imm_index != std::string::npos) {
738 std::ostringstream sreg;
739 sreg << imm;
740 std::string imm_string = sreg.str();
741 base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
742 }
743
744 if (str.size() > 0) {
745 str += "\n";
746 }
747 str += base;
748 }
749 }
750 }
751 // Add a newline at the end.
752 str += "\n";
753 return str;
754 }
755
Andreas Gampe851df202014-11-12 14:05:46 -0800756 template <RegisterView kRegView>
757 std::string GetRegName(const Reg& reg) {
758 std::ostringstream sreg;
759 switch (kRegView) {
760 case RegisterView::kUsePrimaryName:
761 sreg << reg;
762 break;
763
764 case RegisterView::kUseSecondaryName:
765 sreg << GetSecondaryRegisterName(reg);
766 break;
Chao-ying Fud23840d2015-04-07 16:03:04 -0700767
768 case RegisterView::kUseTertiaryName:
769 sreg << GetTertiaryRegisterName(reg);
770 break;
771
772 case RegisterView::kUseQuaternaryName:
773 sreg << GetQuaternaryRegisterName(reg);
774 break;
Andreas Gampe851df202014-11-12 14:05:46 -0800775 }
776 return sreg.str();
777 }
778
779 std::string GetFPRegName(const FPReg& reg) {
780 std::ostringstream sreg;
781 sreg << reg;
782 return sreg.str();
783 }
784
Andreas Gampe849cc5e2014-11-18 13:46:46 -0800785 // If the assembly file needs a header, return it in a sub-class.
786 virtual const char* GetAssemblyHeader() {
787 return nullptr;
788 }
789
790 void WarnOnCombinations(size_t count) {
791 if (count > kWarnManyCombinationsThreshold) {
792 GTEST_LOG_(WARNING) << "Many combinations (" << count << "), test generation might be slow.";
793 }
794 }
795
796 static constexpr const char* REG_TOKEN = "{reg}";
797 static constexpr const char* REG1_TOKEN = "{reg1}";
798 static constexpr const char* REG2_TOKEN = "{reg2}";
Chris Larsendbce0d72015-09-17 13:34:00 -0700799 static constexpr const char* REG3_TOKEN = "{reg3}";
Andreas Gampe849cc5e2014-11-18 13:46:46 -0800800 static constexpr const char* IMM_TOKEN = "{imm}";
801
802 private:
Andreas Gampe851df202014-11-12 14:05:46 -0800803 template <RegisterView kRegView>
804 std::string RepeatRegisterImm(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes,
805 std::string fmt) {
806 const std::vector<Reg*> registers = GetRegisters();
807 std::string str;
808 std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
Andreas Gampe849cc5e2014-11-18 13:46:46 -0800809
810 WarnOnCombinations(registers.size() * imms.size());
811
Andreas Gampe851df202014-11-12 14:05:46 -0800812 for (auto reg : registers) {
813 for (int64_t imm : imms) {
814 Imm new_imm = CreateImmediate(imm);
815 (assembler_.get()->*f)(*reg, new_imm);
816 std::string base = fmt;
817
818 std::string reg_string = GetRegName<kRegView>(*reg);
819 size_t reg_index;
820 while ((reg_index = base.find(REG_TOKEN)) != std::string::npos) {
821 base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string);
822 }
823
824 size_t imm_index = base.find(IMM_TOKEN);
825 if (imm_index != std::string::npos) {
826 std::ostringstream sreg;
827 sreg << imm;
828 std::string imm_string = sreg.str();
829 base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
830 }
831
832 if (str.size() > 0) {
833 str += "\n";
834 }
835 str += base;
836 }
837 }
838 // Add a newline at the end.
839 str += "\n";
840 return str;
841 }
842
Alexey Frunzea0e87b02015-09-24 22:57:20 -0700843 // Override this to pad the code with NOPs to a certain size if needed.
844 virtual void Pad(std::vector<uint8_t>& data ATTRIBUTE_UNUSED) {
845 }
846
Andreas Gampe03b9ee42015-04-24 21:41:45 -0700847 void DriverWrapper(std::string assembly_text, std::string test_name) {
Vladimir Markocf93a5c2015-06-16 11:33:24 +0000848 assembler_->FinalizeCode();
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700849 size_t cs = assembler_->CodeSize();
Ian Rogers700a4022014-05-19 16:49:03 -0700850 std::unique_ptr<std::vector<uint8_t>> data(new std::vector<uint8_t>(cs));
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700851 MemoryRegion code(&(*data)[0], data->size());
852 assembler_->FinalizeInstructions(code);
Alexey Frunzea0e87b02015-09-24 22:57:20 -0700853 Pad(*data);
Andreas Gampe03b9ee42015-04-24 21:41:45 -0700854 test_helper_->Driver(*data, assembly_text, test_name);
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700855 }
856
Andreas Gampe849cc5e2014-11-18 13:46:46 -0800857 static constexpr size_t kWarnManyCombinationsThreshold = 500;
Andreas Gampe851df202014-11-12 14:05:46 -0800858
Ian Rogers700a4022014-05-19 16:49:03 -0700859 std::unique_ptr<Ass> assembler_;
Andreas Gampe03b9ee42015-04-24 21:41:45 -0700860 std::unique_ptr<AssemblerTestInfrastructure> test_helper_;
Andreas Gampe7747c8d2014-08-06 14:53:03 -0700861
Andreas Gampe851df202014-11-12 14:05:46 -0800862 DISALLOW_COPY_AND_ASSIGN(AssemblerTest);
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700863};
864
865} // namespace art
866
867#endif // ART_COMPILER_UTILS_ASSEMBLER_TEST_H_