• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2016, VIXL authors
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are met:
6//
7//   * Redistributions of source code must retain the above copyright notice,
8//     this list of conditions and the following disclaimer.
9//   * Redistributions in binary form must reproduce the above copyright notice,
10//     this list of conditions and the following disclaimer in the documentation
11//     and/or other materials provided with the distribution.
12//   * Neither the name of ARM Limited nor the names of its contributors may be
13//     used to endorse or promote products derived from this software without
14//     specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
17// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27/// This file is a template read by tools/generate_tests.py, it isn't valid C++
28/// as it is. Variables written as ${substitute_me} are replaced by the script.
29/// Comments starting with three forward slashes such as this one are also
30/// removed.
31
32${do_not_edit_comment}
33
34#include "test-runner.h"
35
36#include "test-utils.h"
37#include "test-utils-aarch32.h"
38
39#include "aarch32/assembler-aarch32.h"
40#include "aarch32/macro-assembler-aarch32.h"
41#include "aarch32/disasm-aarch32.h"
42
43#define __ masm.
44#define BUF_SIZE (4096)
45
46#ifdef VIXL_INCLUDE_SIMULATOR_AARCH32
47// Run tests with the simulator.
48
49#define SETUP() MacroAssembler masm(BUF_SIZE)
50
51#define START() masm.GetBuffer()->Reset()
52
53#define END() \
54  __ Hlt(0);  \
55  __ FinalizeCode();
56
57// TODO: Run the tests in the simulator.
58#define RUN()
59
60#define TEARDOWN()
61
62#else  // ifdef VIXL_INCLUDE_SIMULATOR_AARCH32.
63
64#define SETUP()                                   \
65  MacroAssembler masm(BUF_SIZE);                  \
66  UseScratchRegisterScope harness_scratch(&masm); \
67  harness_scratch.ExcludeAll();
68
69#define START()                             \
70  masm.GetBuffer()->Reset();                \
71  __ Push(r4);                              \
72  __ Push(r5);                              \
73  __ Push(r6);                              \
74  __ Push(r7);                              \
75  __ Push(r8);                              \
76  __ Push(r9);                              \
77  __ Push(r10);                             \
78  __ Push(r11);                             \
79  __ Push(lr);                              \
80  harness_scratch.Include(ip);
81
82#define END()                               \
83  harness_scratch.Exclude(ip);              \
84  __ Pop(lr);                               \
85  __ Pop(r11);                              \
86  __ Pop(r10);                              \
87  __ Pop(r9);                               \
88  __ Pop(r8);                               \
89  __ Pop(r7);                               \
90  __ Pop(r6);                               \
91  __ Pop(r5);                               \
92  __ Pop(r4);                               \
93  __ Bx(lr);                                \
94  __ FinalizeCode();
95
96#define RUN()                                                                \
97  {                                                                          \
98    int pcs_offset = masm.IsUsingT32() ? 1 : 0;                              \
99    masm.GetBuffer()->SetExecutable();                                       \
100    ExecuteMemory(masm.GetBuffer()->GetStartAddress<byte*>(),                \
101                  masm.GetSizeOfCodeGenerated(),                             \
102                  pcs_offset);                                               \
103    masm.GetBuffer()->SetWritable();                                         \
104  }
105
106#define TEARDOWN() \
107  harness_scratch.Close();
108
109#endif  // ifdef VIXL_INCLUDE_SIMULATOR_AARCH32
110
111namespace vixl {
112namespace aarch32 {
113
114// List of instruction encodings:
115#define FOREACH_INSTRUCTION(M) \
116  ${instruction_list_declaration}
117
118// The following definitions are defined again in each generated test, therefore
119// we need to place them in an anomymous namespace. It expresses that they are
120// local to this file only, and the compiler is not allowed to share these types
121// across test files during template instantiation. Specifically, `Operands` and
122// `Inputs` have various layouts across generated tests so they absolutely
123// cannot be shared.
124
125#ifdef ${isa_guard}
126namespace {
127
128// Values to be passed to the assembler to produce the instruction under test.
129struct Operands {
130  ${operand_list_declaration}
131};
132
133// Input data to feed to the instruction.
134struct Inputs {
135  ${input_declarations}
136};
137
138// This structure contains all input data needed to test one specific encoding.
139// It used to generate a loop over an instruction.
140struct TestLoopData {
141  // The `operands` fields represents the values to pass to the assembler to
142  // produce the instruction.
143  Operands operands;
144  // Description of the operands, used for error reporting.
145  const char* operands_description;
146  // Unique identifier, used for generating traces.
147  const char* identifier;
148  // Array of values to be fed to the instruction.
149  size_t input_size;
150  const Inputs* inputs;
151};
152
153${input_definitions}
154
155// A loop will be generated for each element of this array.
156const TestLoopData kTests[] = {${test_case_definitions}};
157
158// We record all inputs to the instructions as outputs. This way, we also check
159// that what shouldn't change didn't change.
160struct TestResult {
161  size_t output_size;
162  const Inputs* outputs;
163};
164
165// These headers each contain an array of `TestResult` with the reference output
166// values. The reference arrays are names `kReference{mnemonic}`.
167${include_trace_files}
168
169// The maximum number of errors to report in detail for each test.
170const unsigned kErrorReportLimit = 8;
171
172typedef void (MacroAssembler::*Fn)(${macroassembler_method_args});
173
174void TestHelper(Fn instruction, const char* mnemonic,
175                const TestResult reference[]) {
176  SETUP();
177  ${macroassembler_set_isa}
178  START();
179
180  // Data to compare to `reference`.
181  TestResult* results[ARRAY_SIZE(kTests)];
182
183  // Test cases for memory bound instructions may allocate a buffer and save its
184  // address in this array.
185  byte* scratch_memory_buffers[ARRAY_SIZE(kTests)];
186
187  // Generate a loop for each element in `kTests`. Each loop tests one specific
188  // instruction.
189  for (unsigned i = 0; i < ARRAY_SIZE(kTests); i++) {
190    // Allocate results on the heap for this test.
191    results[i] = new TestResult;
192    results[i]->outputs = new Inputs[kTests[i].input_size];
193    results[i]->output_size = kTests[i].input_size;
194
195    size_t input_stride = sizeof(kTests[i].inputs[0]) * kTests[i].input_size;
196    VIXL_ASSERT(IsUint32(input_stride));
197
198    scratch_memory_buffers[i] = NULL;
199
200    Label loop;
201    UseScratchRegisterScope scratch_registers(&masm);
202    // Include all registers from r0 ro r12.
203    scratch_registers.Include(RegisterList(0x1fff));
204
205    // Values to pass to the macro-assembler.
206    ${code_instantiate_operands}
207
208    // Allocate reserved registers for our own use.
209    Register input_ptr = scratch_registers.Acquire();
210    Register input_end = scratch_registers.Acquire();
211    Register result_ptr = scratch_registers.Acquire();
212
213    // Initialize `input_ptr` to the first element and `input_end` the address
214    // after the array.
215    __ Mov(input_ptr, Operand::From(kTests[i].inputs));
216    __ Add(input_end, input_ptr, static_cast<uint32_t>(input_stride));
217    __ Mov(result_ptr, Operand::From(results[i]->outputs));
218    __ Bind(&loop);
219
220    ${code_prologue}
221
222    (masm.*instruction)(${code_parameter_list});
223
224    ${code_epilogue}
225
226    // Advance the result pointer.
227    __ Add(result_ptr, result_ptr, Operand::From(sizeof(kTests[i].inputs[0])));
228    // Loop back until `input_ptr` is lower than `input_base`.
229    __ Add(input_ptr, input_ptr, Operand::From(sizeof(kTests[i].inputs[0])));
230    __ Cmp(input_ptr, input_end);
231    __ B(ne, &loop);
232  }
233
234  END();
235
236  RUN();
237
238  if (Test::generate_test_trace()) {
239    // Print the results.
240    for (size_t i = 0; i < ARRAY_SIZE(kTests); i++) {
241      printf("const Inputs kOutputs_%s_%s[] = {\n", mnemonic,
242             kTests[i].identifier);
243      for (size_t j = 0; j < results[i]->output_size; j++) {
244        printf("  { ");
245        ${trace_print_outputs}
246        printf(" },\n");
247      }
248      printf("};\n");
249    }
250    printf("const TestResult kReference%s[] = {\n", mnemonic);
251    for (size_t i = 0; i < ARRAY_SIZE(kTests); i++) {
252      printf("  {\n");
253      printf("    ARRAY_SIZE(kOutputs_%s_%s),\n", mnemonic,
254             kTests[i].identifier);
255      printf("    kOutputs_%s_%s,\n", mnemonic, kTests[i].identifier);
256      printf("  },\n");
257    }
258    printf("};\n");
259  } else if (kCheckSimulatorTestResults) {
260    // Check the results.
261    unsigned total_error_count = 0;
262    for (size_t i = 0; i < ARRAY_SIZE(kTests); i++) {
263      bool instruction_has_errors = false;
264      for (size_t j = 0; j < kTests[i].input_size; j++) {
265        ${check_instantiate_results}
266        ${check_instantiate_inputs}
267        ${check_instantiate_references}
268
269        if ((${check_results_against_references}) &&
270            (++total_error_count <= kErrorReportLimit)) {
271          // Print the instruction once even if it triggered multiple failures.
272          if (!instruction_has_errors) {
273            printf("Error(s) when testing \"%s %s\":\n", mnemonic,
274                   kTests[i].operands_description);
275            instruction_has_errors = true;
276          }
277          // Print subsequent errors.
278          printf("  Input:    ");
279          ${check_print_input}
280          printf("\n");
281          printf("  Expected: ");
282          ${check_print_expected}
283          printf("\n");
284          printf("  Found:    ");
285          ${check_print_found}
286          printf("\n\n");
287        }
288      }
289    }
290
291    if (total_error_count > kErrorReportLimit) {
292      printf("%u other errors follow.\n",
293             total_error_count - kErrorReportLimit);
294    }
295    VIXL_CHECK(total_error_count == 0);
296  } else {
297    VIXL_WARNING("Assembled the code, but did not run anything.\n");
298  }
299
300  for (size_t i = 0; i < ARRAY_SIZE(kTests); i++) {
301    delete[] results[i]->outputs;
302    delete results[i];
303    delete[] scratch_memory_buffers[i];
304  }
305
306  TEARDOWN();
307}
308
309// Instantiate tests for each instruction in the list.
310// TODO: Remove this limitation by having a sandboxing mechanism.
311#if defined(VIXL_HOST_POINTER_32)
312#define TEST(mnemonic)                                                      \
313  void Test_##mnemonic() {                                                  \
314    TestHelper(&MacroAssembler::mnemonic, #mnemonic, kReference##mnemonic); \
315  }                                                                         \
316  Test test_##mnemonic("AARCH32_${test_name}_" #mnemonic,                   \
317                       &Test_##mnemonic);
318#else
319#define TEST(mnemonic)                                                      \
320  void Test_##mnemonic() {                                                  \
321    VIXL_WARNING("This test can only run on a 32-bit host.\n");             \
322    USE(TestHelper);                                                        \
323  }                                                                         \
324  Test test_##mnemonic("AARCH32_${test_name}_" #mnemonic,                   \
325                       &Test_##mnemonic);
326#endif
327
328FOREACH_INSTRUCTION(TEST)
329#undef TEST
330
331}  // namespace
332#endif
333
334}  // namespace aarch32
335}  // namespace vixl
336