huangcm
2024-12-18 9d29be7f7249789d6ffd0440067187a9f040c2cd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
# Copyright 2016, VIXL authors
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#   * Redistributions of source code must retain the above copyright notice,
#     this list of conditions and the following disclaimer.
#   * Redistributions in binary form must reproduce the above copyright notice,
#     this list of conditions and the following disclaimer in the documentation
#     and/or other materials provided with the distribution.
#   * Neither the name of ARM Limited nor the names of its contributors may be
#     used to endorse or promote products derived from this software without
#     specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
import itertools
import random
import os.path
from copy import deepcopy
 
class OperandList(object):
  """
  Convenience class representing a list of operand objects. It can be viewed is
  an iterator over operand objects.
 
  Attributes:
    operand_list
  """
 
  def __init__(self, operand_list):
    self.operand_list = operand_list
 
  def __iter__(self):
    return iter(self.operand_list)
 
  def unwrap(self):
    """
    Return a list of `Operand` objects, unwrapping `OperandWrapper` objects into
    `Operand` objects. For example:
 
    ~~~
    Condition, Register, Operand(Register, Shift, Register)
    ~~~
 
    Unwraps to:
 
    ~~~
    Condition, Register, Register, Shift, Register
    ~~~
    """
    return itertools.chain(*self.operand_list)
 
  def ExcludeVariants(self, type_name, variant_to_exclude):
    """
    Remove variants in `variant_to_exclude` from operands with type `type_name`.
    """
    # Find the list of operand with type `type_name`.
    relevant_operands = filter(lambda operand: operand.type_name == type_name,
                               self)
    for operand in relevant_operands:
      # Remove the intersection of the existing variants and variants we do not
      # want.
      for variant in set(operand.variants) & set(variant_to_exclude):
        operand.variants.remove(variant)
 
  def GetNames(self):
    """
    Return the list of all `Operand` names, excluding `OperandWrapper` objects.
    """
    return [operand.name for operand in self.unwrap()]
 
 
class InputList(object):
  """
  Convevience class representing a list of input objects.
 
  This class is an iterator over input objects.
 
  Attributes:
    inputs
  """
 
  def __init__(self, inputs):
    self.inputs = inputs
 
  def __iter__(self):
    return iter(self.inputs)
 
  def GetNames(self):
    """
    Return the list of input names.
    """
    return [input.name for input in self]
 
 
class TestCase(object):
  """
  Object representation of a test case, as described in JSON. This object is
  used to build sets of operands and inputs that will be used by the generator
  to produce C++ arrays.
 
  Attributes:
    name            Name of the test case, it is used to name the array to
                    produce.
    seed            Seed value to use for reproducable random generation.
    operand_names   List of operand names this test case covers.
    input_names     List of input names this test case covers.
    operand_filter  Python expression as a string to filter out operands.
    input_filter    Python expression as a string to filter out inputs.
    operand_limit   Optional limit of the number of operands to generate.
    input_limit     Optional limit of the number of inputs to generate.
    it_condition    If not None, an IT instruction needs to be generated for the
                    instruction under test to be valid. This member is a string
                    template indicating the name of the condition operand, to be
                    used with "format". For example, it will most likely have
                    the value "{cond}".
  """
 
  # Declare functions that will be callable from Python expressions in
  # `self.operand_filter`.
  operand_filter_runtime = {
      'register_is_low': lambda register:
          register in ["r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7"]
  }
 
  def __init__(self, name, seed, operand_names, input_names, operand_filter,
               input_filter, operand_limit, input_limit, it_condition):
    self.name = name
    self.seed = seed
    self.operand_names = operand_names
    self.input_names = input_names
    self.operand_filter = operand_filter
    self.input_filter = input_filter
    self.operand_limit = operand_limit
    self.input_limit = input_limit
    self.it_condition = it_condition
 
  def GenerateOperands(self, operand_types):
    """
    Generate a list of tuples, each tuple describing what operands to pass to an
    instruction to encode it. We use this to generate operand definitions.
 
    The algorithm used is a simple product of all operand variants. To limit
    what we generate, we choose to apply the product only on operands with their
    name in the `self.operand_names` list.
 
    Additionally, we use the Python expression in `self.operand_filter` to
    filter out tuples we do not want.
 
    Argument:
      operand_types  The `OperandList` object that describe the form of the
                     instruction to generate code for.
    """
    # Build a list of all possible variants as a list of tuples. If the
    # operand's name is not in `self.operand_names`, then we restrict the list
    # to contain default variant. Each tuple in the list has the form
    # `(name, [variant1, variant2, ...])`. For example:
    #
    #   [
    #     ('cond', ['al', 'ne', 'eq', ...]), # All condition variants.
    #     ('rd', ['r0', 'r1', ...]),         # All register variants.
    #     ('rn', ['r0'])                     # Default register variant (r0).
    #     ...
    #   ]
    variants = [
        [(operand_type.name, variant) for variant in operand_type.variants]
            if operand_type.name in self.operand_names
            else [(operand_type.name, operand_type.default)]
        for operand_type in operand_types.unwrap()
    ]
    lambda_string = "lambda {args}: {expression}".format(
        args=",".join(operand_types.GetNames()),
        expression=self.operand_filter)
    filter_lambda = eval(lambda_string, self.operand_filter_runtime)
 
    def BuildOperandDefinition(operands):
      """
      Take a list of tuples describing the operands and build a definition from
      it. A definition is a tuple with a list of variants and a
      `expect_instruction_before` string.
 
      For example, we are turning this:
 
        [
          ('cond', 'ne'),
          ('rd', 'r0'),
          ('rn', 'r1'),
          ('rm', 'r0)
        [
 
      Into:
 
        (['ne', 'r0', 'r1', 'r0'], "It ne;")
 
      """
      return (
        # Build a list of operands by only keeping the second element of each
        # tuple.
        [operand[1] for operand in operands],
        # The next field is a boolean indicating if the test case needs to
        # generate an IT instruction.
        "true" if self.it_condition else "false",
        # If so, what condition should it be?
        self.it_condition.format(**dict(operands)) if self.it_condition else "al"
      )
 
    # Build and return a list of operand definitions by computing the product of
    # all variants and filtering them with `filter_lambda`.
    #
    # Operand definitions consist of a list with a list of variants and an
    # optional `expect_instruction_before` string. For example:
    #
    #   [
    #     (['al', 'r0', 'r1', 'r2'], ""),
    #     (['ne', 'r0', 'r1', 'r0'], "It ne;"),
    #     ...
    #   ]
    #
    # Here, the filtered product of variants builds a list of lists of tuples, as such:
    #
    #   [
    #     [('cond', 'al'), ('rd', 'r0'), ('rn', 'r1'), ('rn', 'r2')]
    #     [('cond', 'ne'), ('rd', 'r0'), ('rn', 'r1'), ('rn', 'r0')],
    #     ...
    #   ]
    #
    # We then pass them to `BuildOperandDefinition` to produce the expected form
    # out of it.
    result = [
        BuildOperandDefinition(operands)
        for operands in itertools.product(*variants)
        if filter_lambda(**dict(operands))
    ]
    if self.operand_limit is None:
      return result
    else:
      # Use a fixed seed to randomly choose a limited set of operands.
      random.seed(self.seed)
      return random.sample(result, self.operand_limit)
 
  def GenerateInputs(self, input_types):
    """
    Generate a list of tuples, each tuple describing what input to pass to an
    instruction at runtime. We use this to generate input definitions.
 
    The algorithm used is a simple product of all input values. To limit what
    we generate, we choose to apply the product only on inputs with their name
    in the `self.input_names` list.
 
    Additionally, we use the Python expression in `self.input_filter` to filter
    out tuples we do not want.
 
    Argument:
      input_types  The `InputList` object describing the list of inputs the
                   instruction can take.
    """
    # Build a list of all possible values as a list of lists. If the input's
    # name is not in `self.input_names`, then we restrict the list to the
    # default value.
    values = [
        input_type.values
            if input_type.name in self.input_names else [input_type.default]
        for input_type in input_types
    ]
    lambda_string = "lambda {args}: {expression}".format(
        args=", ".join(input_types.GetNames()),
        expression=self.input_filter)
    filter_lambda = eval(lambda_string)
    # Build and return a list of input definitions, such as
    # [('NoFlag', '0xffffffff', 0xabababab'), ...] for example.
    result = [
        input_definition
        for input_definition in itertools.product(*values)
        if filter_lambda(*input_definition)
    ]
    if self.input_limit is None:
      return result
    else:
      # Use a fixed seed to randomly choose a limited set of inputs.
      random.seed(self.seed)
      return random.sample(result, self.input_limit)
 
 
class Generator(object):
  """
  A `Generator` object contains all information needed to generate a test file.
  Each method will return a string used to fill a variable in a template.
 
 
  Attributes:
    test_name  Name of the test inferred from the name of the configuration
               file. It has the following form: `type-op1-op2-op3-isa`.
    test_isa   Instruction set supported by the test, either 'a32' or 't32'.
    test_type  Type of the test, extracted from test_name.
    mnemonics  List of instruction mnemonics.
    operands   `OperandList` object.
    inputs     `InputList` object.
    test_cases  List of `TestCase` objects.
  """
 
  def __init__(self, test_name, test_isa, test_type, mnemonics, operands,
               inputs, test_cases):
    self.test_name = test_name
    self.test_isa = test_isa
    self.test_type = test_type
    self.mnemonics = mnemonics
    self.inputs = inputs
    self.test_cases = test_cases
 
    # A simulator test cannot easily make use of the PC and SP registers.
    if self.test_type == "simulator":
      # We need to explicitely create our own deep copy the operands before we
      # can modify them.
      self.operands = deepcopy(operands)
      self.operands.ExcludeVariants("Register", ["r13", "r15"])
    else:
      self.operands = operands
 
  def MnemonicToMethodName(self, mnemonic):
    if self.test_type in ["simulator", "macro-assembler"]:
      # Return a MacroAssembler method name
      return mnemonic.capitalize()
    else:
      # Return an Assembler method name
      method_name = mnemonic.lower()
      return "and_" if method_name == "and" else method_name
 
  def InstructionListDeclaration(self):
    """
    ~~~
    M(Adc)  \
    M(Adcs) \
    M(Add)  \
    M(Adds) \
    M(And)  \
    ...
    ~~~
    """
    return "".join([
      "M({}) \\\n".format(self.MnemonicToMethodName(mnemonic))
      for mnemonic in self.mnemonics
    ])
 
  def OperandDeclarations(self):
    """
    ~~~
    Condition cond;
    Register rd;
    Register rn;
    ...
    ~~~
    """
    return "".join([operand.Declare() for operand in self.operands])
 
  def InputDeclarations(self):
    """
    ~~~
    uint32_t cond;
    uint32_t rd;
    uint32_t rn;
    ...
    ~~~
    """
    return "".join([input.Declare() for input in self.inputs])
 
  def InputDefinitions(self):
    """
    ~~~
    static const Inputs kCondition[] = {{...},{...}, ...};
    static const Inputs kRdIsRd[] = {{...},{...}, ...};
    ...
    ~~~
    """
    def InputDefinition(test_input):
      inputs = [
          "{{{}}}".format(",".join(input))
          for input in test_input.GenerateInputs(self.inputs)
      ]
 
      return """static const Inputs k{name}[] = {{ {input} }};
          """.format(name=test_input.name, input=",".join(inputs))
 
    return "\n".join(map(InputDefinition, self.test_cases))
 
  def TestCaseDefinitions(self):
    """
    For simulator tests:
    ~~~
    {{eq, r0, r0, ...},
     "eq r0 r0 ...",
     "Condition_eq_r0_...",
     ARRAY_SIZE(kCondition), kCondition},
    ...
    {{eq, r0, r0, ...},
     "eq r0 r0 ...",
     "RdIsRd_eq_r0_...",
     ARRAY_SIZE(kRdIsRd), kRdIsRn},
    ...
    ~~~
 
    For assembler tests:
    ~~~
    {{eq, r0, r0, ...},
     "",
     "eq r0 r0 ...",
     "Condition_eq_r0_...",
    ...
    {{eq, r0, r0, ...},
     "",
     "eq r0 r0 ...",
     "RdIsRd_eq_r0_..."}
    ...
    {{eq, r0, r0, ...},
     "It eq",
     "eq r0 r0 ...",
     "RdIsRd_eq_r0_..."}
    ...
    ~~~
    """
    def SimulatorTestCaseDefinition(test_case):
      test_cases = [
          """{{ {{ {operands} }},
             "{operands_description}",
             "{identifier}",
             ARRAY_SIZE(k{test_case_name}),
             k{test_case_name} }}
              """.format(operands=",".join(operand),
                         operands_description=" ".join(operand),
                         identifier=test_case.name + "_" + "_".join(operand),
                         test_case_name=test_case.name)
          for operand, _, _ in test_case.GenerateOperands(self.operands)
      ]
      return ",\n".join(test_cases)
 
    def AssemblerTestCaseDefinition(test_case):
      test_cases = [
          """{{ {{ {operands} }},
             {in_it_block},
             {it_condition},
             "{operands_description}",
             "{identifier}" }}
              """.format(operands=",".join(operand),
                         in_it_block=in_it_block,
                         it_condition=it_condition,
                         operands_description=" ".join(operand),
                         identifier="_".join(operand))
          for operand, in_it_block, it_condition
              in test_case.GenerateOperands(self.operands)
      ]
      return ",\n".join(test_cases)
 
    def MacroAssemblerTestCaseDefinition(test_case):
      test_cases = [
          """{{ {{ {operands} }},
             "{operands_description}",
             "{identifier}" }}
              """.format(operands=",".join(operand),
                         operands_description=", ".join(operand),
                         identifier="_".join(operand))
          for operand, _, _ in test_case.GenerateOperands(self.operands)
      ]
      return ",\n".join(test_cases)
 
    if self.test_type == "simulator":
      return ",\n".join(map(SimulatorTestCaseDefinition, self.test_cases))
    elif self.test_type == "assembler":
      return ",\n".join(map(AssemblerTestCaseDefinition, self.test_cases))
    elif self.test_type == "macro-assembler":
      return ",\n".join(map(MacroAssemblerTestCaseDefinition, self.test_cases))
    elif self.test_type == "assembler-negative":
      return ",\n".join(map(MacroAssemblerTestCaseDefinition, self.test_cases))
    else:
      raise Exception("Unrecognized test type \"{}\".".format(self.test_type))
 
  def IncludeTraceFiles(self):
    """
    ~~~
    #include "aarch32/traces/sim-...-a32.h"
    #include "aarch32/traces/sim-...-a32.h"
    ...
    ~~~
    """
    operands = "-".join(self.operands.GetNames())
    return "".join([
        "#include \"aarch32/traces/" + self.GetTraceFileName(mnemonic) + "\"\n"
        for mnemonic in self.mnemonics
    ])
 
  def MacroAssemblerMethodArgs(self):
    """
    ~~~
    Condition cond, Register rd, Register rm, const Operand& immediate
    ~~~
    """
    return ", ".join([
        operand.GetArgumentType() + " " + operand.name
        for operand in self.operands
    ])
 
  def MacroAssemblerSetISA(self):
    """
    Generate code to set the ISA.
    """
    if self.test_isa == "t32":
      return "masm.UseT32();"
    else:
      return "masm.UseA32();"
 
  def CodeInstantiateOperands(self):
    """
    ~~~
    Condition cond = kTests[i].operands.cond;
    Register rd = kTests[i].operands.rd;
    ...
    ~~~
    """
    code = "".join([operand.Instantiate() for operand in self.operands])
    if self.test_type in ["simulator", "macro-assembler"]:
      # Simulator tests need scratch registers to function and uses
      # `UseScratchRegisterScope` to dynamically allocate them. We need to
      # exclude all register operands from the list of available scratch
      # registers.
      # MacroAssembler tests also need to ensure that they don't try to run tests
      # with registers that are scratch registers; the MacroAssembler contains
      # assertions to protect against such usage.
      excluded_registers = [
          "scratch_registers.Exclude({});".format(operand.name)
          for operand in self.operands.unwrap()
          if operand.type_name == "Register"
      ]
      return code + "\n".join(excluded_registers)
    return code
 
  def CodePrologue(self):
    """
    ~~~
    __ Ldr(rn, MemOperand(input_ptr, offsetof(Inputs, rn)));
    __ Ldr(rm, MemOperand(input_ptr, offsetof(Inputs, rm)));
    ...
    ~~~
    """
    return "".join([input.Prologue() for input in self.inputs])
 
  def CodeEpilogue(self):
    """
    ~~~
    __ Str(rn, MemOperand(result_ptr, offsetof(Inputs, rn)));
    __ Str(rm, MemOperand(result_ptr, offsetof(Inputs, rm)));
    ...
    ~~~
    """
    return "".join([input.Epilogue() for input in self.inputs])
 
  def CodeParameterList(self):
    """
    ~~~
    cond, rd, rn, immediate
    ~~~
    """
    return ", ".join([
        operand.name
        for operand in self.operands
    ])
 
  def TracePrintOutputs(self):
    """
    ~~~
    printf("0x%08" PRIx32, results[i]->outputs[j].cond);
    printf(", ");
    printf("0x%08" PRIx32, results[i]->outputs[j].rd);
    printf(", ");
    ...
    ~~~
    """
    return "printf(\", \");".join(
        [input.PrintOutput() for input in self.inputs])
 
 
  def CheckInstantiateResults(self):
    """
    ~~~
    uint32_t cond = results[i]->outputs[j].cond;
    uint32_t rd = results[i]->outputs[j].rd;
    ...
    ~~~
    """
    return "".join([input.InstantiateResult() for input in self.inputs])
 
  def CheckInstantiateInputs(self):
    """
    ~~~
    uint32_t cond_input = kTests[i].inputs[j].cond;
    uint32_t rd_input = kTests[i].inputs[j].rd;
    ...
    ~~~
    """
    return "".join([input.InstantiateInput("_input") for input in self.inputs])
 
  def CheckInstantiateReferences(self):
    """
    ~~~
    uint32_t cond_ref = reference[i].outputs[j].cond;
    uint32_t rd_ref = reference[i].outputs[j].rd;
    ...
    ~~~
    """
    return "".join([input.InstantiateReference("_ref") for input in self.inputs])
 
  def CheckResultsAgainstReferences(self):
    """
    ~~~
    (cond != cond_ref) || (rd != rd_ref) || ...
    ~~~
    """
    return " || ".join([input.Compare("", "!=", "_ref") for input in self.inputs])
 
  def CheckPrintInput(self):
    """
    ~~~
    printf("0x%08" PRIx32, cond_input);
    printf(", ");
    printf("0x%08" PRIx32, rd_input);
    printf(", ");
    ...
    ~~~
    """
    return "printf(\", \");".join(
        [input.PrintInput("_input") for input in self.inputs])
 
  def CheckPrintExpected(self):
    """
    ~~~
    printf("0x%08" PRIx32, cond_ref);
    printf(", ");
    printf("0x%08" PRIx32, rd_ref);
    printf(", ");
    ...
    ~~~
    """
    return "printf(\", \");".join(
        [input.PrintInput("_ref") for input in self.inputs])
 
  def CheckPrintFound(self):
    """
    ~~~
    printf("0x%08" PRIx32, cond);
    printf(", ");
    printf("0x%08" PRIx32, rd);
    printf(", ");
    ...
    ~~~
    """
    return "printf(\", \");".join(
        [input.PrintInput("") for input in self.inputs])
 
  def TestName(self):
    """
    ~~~
    SIMULATOR_COND_RD_RN_RM_...
    ~~~
    """
    return self.test_type.replace("-", "_").upper() + "_" + \
        self.test_name.replace("-", "_").upper()
 
  def TestISA(self):
    return self.test_isa.upper()
 
  def GetTraceFileName(self, mnemonic):
    """
    Return the name of a trace file for a given mnemonic.
    """
    return self.test_type + "-" + self.test_name + "-" + \
        mnemonic.lower() + "-" + self.test_isa + ".h"
 
  def WriteEmptyTraces(self, output_directory):
    """
    Write out empty trace files so we can compile the new test cases.
    """
    for mnemonic in self.mnemonics:
      # The MacroAssembler and negative assembler tests have no traces.
      if self.test_type in ["macro-assembler", "assembler-negative"]: continue
 
      with open(os.path.join(output_directory, self.GetTraceFileName(mnemonic)),
                "w") as f:
        code = "static const TestResult *kReference{} = NULL;\n"
        f.write(code.format(self.MnemonicToMethodName(mnemonic)))
 
  def GetIsaGuard(self):
    """
    This guard ensure the ISA of the test is enabled.
    """
    if self.test_isa == 'a32':
      return 'VIXL_INCLUDE_TARGET_A32'
    else:
      assert self.test_isa == 't32'
      return 'VIXL_INCLUDE_TARGET_T32'