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