xref: /aosp_15_r20/external/vixl/tools/test_generator/parser.py (revision f5c631da2f1efdd72b5fd1e20510e4042af13d77)
1*f5c631daSSadaf Ebrahimi# Copyright 2016, VIXL authors
2*f5c631daSSadaf Ebrahimi# All rights reserved.
3*f5c631daSSadaf Ebrahimi#
4*f5c631daSSadaf Ebrahimi# Redistribution and use in source and binary forms, with or without
5*f5c631daSSadaf Ebrahimi# modification, are permitted provided that the following conditions are met:
6*f5c631daSSadaf Ebrahimi#
7*f5c631daSSadaf Ebrahimi#   * Redistributions of source code must retain the above copyright notice,
8*f5c631daSSadaf Ebrahimi#     this list of conditions and the following disclaimer.
9*f5c631daSSadaf Ebrahimi#   * Redistributions in binary form must reproduce the above copyright notice,
10*f5c631daSSadaf Ebrahimi#     this list of conditions and the following disclaimer in the documentation
11*f5c631daSSadaf Ebrahimi#     and/or other materials provided with the distribution.
12*f5c631daSSadaf Ebrahimi#   * Neither the name of ARM Limited nor the names of its contributors may be
13*f5c631daSSadaf Ebrahimi#     used to endorse or promote products derived from this software without
14*f5c631daSSadaf Ebrahimi#     specific prior written permission.
15*f5c631daSSadaf Ebrahimi#
16*f5c631daSSadaf Ebrahimi# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
17*f5c631daSSadaf Ebrahimi# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18*f5c631daSSadaf Ebrahimi# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19*f5c631daSSadaf Ebrahimi# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20*f5c631daSSadaf Ebrahimi# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*f5c631daSSadaf Ebrahimi# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22*f5c631daSSadaf Ebrahimi# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23*f5c631daSSadaf Ebrahimi# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24*f5c631daSSadaf Ebrahimi# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25*f5c631daSSadaf Ebrahimi# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*f5c631daSSadaf Ebrahimi
27*f5c631daSSadaf Ebrahimiimport json
28*f5c631daSSadaf Ebrahimiimport re
29*f5c631daSSadaf Ebrahimiimport os
30*f5c631daSSadaf Ebrahimiimport hashlib
31*f5c631daSSadaf Ebrahimiimport collections
32*f5c631daSSadaf Ebrahimiimport itertools
33*f5c631daSSadaf Ebrahimi
34*f5c631daSSadaf Ebrahimifrom test_generator import data_types
35*f5c631daSSadaf Ebrahimifrom test_generator import generator
36*f5c631daSSadaf Ebrahimi
37*f5c631daSSadaf Ebrahimiclass DataTypeBuilder(object):
38*f5c631daSSadaf Ebrahimi  """
39*f5c631daSSadaf Ebrahimi  Factory object for building `data_types.Operand` and `data_types.Input`
40*f5c631daSSadaf Ebrahimi  objects. This object stores information about all operand and input types
41*f5c631daSSadaf Ebrahimi  described in JSON as dictionnaries indexed by their identifier. See
42*f5c631daSSadaf Ebrahimi  `test/a32/config/data-types.json` as a reference.
43*f5c631daSSadaf Ebrahimi
44*f5c631daSSadaf Ebrahimi  Attributes:
45*f5c631daSSadaf Ebrahimi    operand_types    Dictionnary of type names corresponding to the JSON
46*f5c631daSSadaf Ebrahimi                     "type" field.
47*f5c631daSSadaf Ebrahimi    operand_variants Dictionnary of (variants, default) tuples.
48*f5c631daSSadaf Ebrahimi
49*f5c631daSSadaf Ebrahimi    input_types      Dictionnary of type names corresponding to the JSON
50*f5c631daSSadaf Ebrahimi                     "type" field.
51*f5c631daSSadaf Ebrahimi    input_values     Dictionnary of (values, default) tuples.
52*f5c631daSSadaf Ebrahimi  """
53*f5c631daSSadaf Ebrahimi
54*f5c631daSSadaf Ebrahimi  def __init__(self, operand_types, operand_variants, input_types,
55*f5c631daSSadaf Ebrahimi               input_values):
56*f5c631daSSadaf Ebrahimi    self.operand_types = operand_types
57*f5c631daSSadaf Ebrahimi    self.operand_variants = operand_variants
58*f5c631daSSadaf Ebrahimi    self.input_types = input_types
59*f5c631daSSadaf Ebrahimi    self.input_values = input_values
60*f5c631daSSadaf Ebrahimi
61*f5c631daSSadaf Ebrahimi  def BuildOperand(self, name, identifier):
62*f5c631daSSadaf Ebrahimi    """
63*f5c631daSSadaf Ebrahimi    Build a `data_types.Operand` object with the name `name`. `identifier`
64*f5c631daSSadaf Ebrahimi    identifies which type we want to create, as declared in JSON.
65*f5c631daSSadaf Ebrahimi    """
66*f5c631daSSadaf Ebrahimi    type_name = self.operand_types[identifier]
67*f5c631daSSadaf Ebrahimi    variants, default = self.operand_variants[identifier]
68*f5c631daSSadaf Ebrahimi    # We simply pass the `type_name` as a parameter which will be used verbatim
69*f5c631daSSadaf Ebrahimi    # in the code.
70*f5c631daSSadaf Ebrahimi    return data_types.Operand(name, type_name, variants, default)
71*f5c631daSSadaf Ebrahimi
72*f5c631daSSadaf Ebrahimi  def BuildInput(self, name, identifier):
73*f5c631daSSadaf Ebrahimi    """
74*f5c631daSSadaf Ebrahimi    Build a `data_types.Input` object with the name `name`. `identifier`
75*f5c631daSSadaf Ebrahimi    identifies which type we want to create, as declared in JSON.
76*f5c631daSSadaf Ebrahimi    """
77*f5c631daSSadaf Ebrahimi    type_name = self.input_types[identifier]
78*f5c631daSSadaf Ebrahimi    values, default = self.input_values[identifier]
79*f5c631daSSadaf Ebrahimi    # For `data_types.Input` types, the `type_name` refers to the actual name of
80*f5c631daSSadaf Ebrahimi    # the Python class, inheriting from `Input`. This is done so that different
81*f5c631daSSadaf Ebrahimi    # input types can generate different C++ code by overriding the `Load` and
82*f5c631daSSadaf Ebrahimi    # `Store` methods.
83*f5c631daSSadaf Ebrahimi    input_constructor = getattr(data_types, type_name)
84*f5c631daSSadaf Ebrahimi    return input_constructor(name, values, default)
85*f5c631daSSadaf Ebrahimi
86*f5c631daSSadaf Ebrahimi
87*f5c631daSSadaf Ebrahimidef LoadJSON(filename):
88*f5c631daSSadaf Ebrahimi  """
89*f5c631daSSadaf Ebrahimi  Read `filename`, strip its comments and load as JSON.
90*f5c631daSSadaf Ebrahimi  """
91*f5c631daSSadaf Ebrahimi  with open(filename, "r") as f:
92*f5c631daSSadaf Ebrahimi    match_cpp_comments = re.compile("//.*\n")
93*f5c631daSSadaf Ebrahimi    # The order in which structures are described in JSON matters as we use them
94*f5c631daSSadaf Ebrahimi    # as a seed. Computing a hash from a unordered dict always gives a different
95*f5c631daSSadaf Ebrahimi    # value. We use the `object_pairs_hook` to make the json module create
96*f5c631daSSadaf Ebrahimi    # `OrderedDict` objects instead of builtin `dict` objects.
97*f5c631daSSadaf Ebrahimi    return json.loads(match_cpp_comments.sub("", f.read()),
98*f5c631daSSadaf Ebrahimi                      object_pairs_hook=collections.OrderedDict)
99*f5c631daSSadaf Ebrahimi
100*f5c631daSSadaf Ebrahimi
101*f5c631daSSadaf Ebrahimidef ParseDataTypes(json_data_types):
102*f5c631daSSadaf Ebrahimi  """
103*f5c631daSSadaf Ebrahimi  Build a `DataTypeBuilder` object containing all information from the JSON
104*f5c631daSSadaf Ebrahimi  description in `json_data_types`.
105*f5c631daSSadaf Ebrahimi
106*f5c631daSSadaf Ebrahimi  ~~~
107*f5c631daSSadaf Ebrahimi  {
108*f5c631daSSadaf Ebrahimi    "operands": [
109*f5c631daSSadaf Ebrahimi      {
110*f5c631daSSadaf Ebrahimi        "identifier": "AllRegistersButPC"
111*f5c631daSSadaf Ebrahimi        "type": "Register"
112*f5c631daSSadaf Ebrahimi        "variants": [
113*f5c631daSSadaf Ebrahimi          "r0",
114*f5c631daSSadaf Ebrahimi          "r1",
115*f5c631daSSadaf Ebrahimi          "r2",
116*f5c631daSSadaf Ebrahimi          "r3"
117*f5c631daSSadaf Ebrahimi        ]
118*f5c631daSSadaf Ebrahimi        "default": "r0"
119*f5c631daSSadaf Ebrahimi      },
120*f5c631daSSadaf Ebrahimi      {
121*f5c631daSSadaf Ebrahimi        ...
122*f5c631daSSadaf Ebrahimi      }
123*f5c631daSSadaf Ebrahimi    ],
124*f5c631daSSadaf Ebrahimi    "inputs": [
125*f5c631daSSadaf Ebrahimi      {
126*f5c631daSSadaf Ebrahimi        "identifier": "Register"
127*f5c631daSSadaf Ebrahimi        "type": "Register"
128*f5c631daSSadaf Ebrahimi        "values": [
129*f5c631daSSadaf Ebrahimi          "0x00000000",
130*f5c631daSSadaf Ebrahimi          "0xffffffff",
131*f5c631daSSadaf Ebrahimi          "0xabababab"
132*f5c631daSSadaf Ebrahimi        ]
133*f5c631daSSadaf Ebrahimi        "default": "0xabababab"
134*f5c631daSSadaf Ebrahimi      },
135*f5c631daSSadaf Ebrahimi      {
136*f5c631daSSadaf Ebrahimi        ...
137*f5c631daSSadaf Ebrahimi      }
138*f5c631daSSadaf Ebrahimi    ]
139*f5c631daSSadaf Ebrahimi  }
140*f5c631daSSadaf Ebrahimi  ~~~
141*f5c631daSSadaf Ebrahimi  """
142*f5c631daSSadaf Ebrahimi  operand_types = {
143*f5c631daSSadaf Ebrahimi      json_operand_type["identifier"]: json_operand_type["type"]
144*f5c631daSSadaf Ebrahimi      for json_operand_type in json_data_types["operands"]
145*f5c631daSSadaf Ebrahimi  }
146*f5c631daSSadaf Ebrahimi  operand_variants = {
147*f5c631daSSadaf Ebrahimi      json_operand_type["identifier"]:
148*f5c631daSSadaf Ebrahimi          (json_operand_type["variants"], json_operand_type["default"])
149*f5c631daSSadaf Ebrahimi      for json_operand_type in json_data_types["operands"]
150*f5c631daSSadaf Ebrahimi  }
151*f5c631daSSadaf Ebrahimi  input_types = {
152*f5c631daSSadaf Ebrahimi      json_input_type["identifier"]: json_input_type["type"]
153*f5c631daSSadaf Ebrahimi      for json_input_type in json_data_types["inputs"]
154*f5c631daSSadaf Ebrahimi  }
155*f5c631daSSadaf Ebrahimi  input_values = {
156*f5c631daSSadaf Ebrahimi      json_input_type["identifier"]:
157*f5c631daSSadaf Ebrahimi          (json_input_type["values"], json_input_type["default"])
158*f5c631daSSadaf Ebrahimi      for json_input_type in json_data_types["inputs"]
159*f5c631daSSadaf Ebrahimi  }
160*f5c631daSSadaf Ebrahimi  return DataTypeBuilder(operand_types, operand_variants, input_types, input_values)
161*f5c631daSSadaf Ebrahimi
162*f5c631daSSadaf Ebrahimi
163*f5c631daSSadaf Ebrahimidef ParseDescription(data_type_builder, json_description):
164*f5c631daSSadaf Ebrahimi  """
165*f5c631daSSadaf Ebrahimi  Parse the instruction description into a
166*f5c631daSSadaf Ebrahimi  (`generator.OperandList`, `generator.InputList`) tuple.
167*f5c631daSSadaf Ebrahimi
168*f5c631daSSadaf Ebrahimi  Example for an instruction that takes a condidition code, two registers and an
169*f5c631daSSadaf Ebrahimi  immediate as operand. It will also need inputs for the registers, as well as
170*f5c631daSSadaf Ebrahimi  NZCV flags.
171*f5c631daSSadaf Ebrahimi  ~~~
172*f5c631daSSadaf Ebrahimi  {
173*f5c631daSSadaf Ebrahimi    "operands": [
174*f5c631daSSadaf Ebrahimi      {
175*f5c631daSSadaf Ebrahimi        "name": "cond",
176*f5c631daSSadaf Ebrahimi        "type": "Condition",
177*f5c631daSSadaf Ebrahimi      },
178*f5c631daSSadaf Ebrahimi      {
179*f5c631daSSadaf Ebrahimi        "name": "rd",
180*f5c631daSSadaf Ebrahimi        "type": "RegisterScratch",
181*f5c631daSSadaf Ebrahimi      },
182*f5c631daSSadaf Ebrahimi      {
183*f5c631daSSadaf Ebrahimi        "name": "rn",
184*f5c631daSSadaf Ebrahimi        "type": "RegisterScratch",
185*f5c631daSSadaf Ebrahimi      },
186*f5c631daSSadaf Ebrahimi      // The last operand needs to be wrapped into a C++ `Operand` object. We
187*f5c631daSSadaf Ebrahimi      // declare the operands that need to be wrapped as a list.
188*f5c631daSSadaf Ebrahimi      {
189*f5c631daSSadaf Ebrahimi        "name": "op",
190*f5c631daSSadaf Ebrahimi        "wrapper": "Operand",
191*f5c631daSSadaf Ebrahimi        "operands": [
192*f5c631daSSadaf Ebrahimi          {
193*f5c631daSSadaf Ebrahimi            "name": "immediate",
194*f5c631daSSadaf Ebrahimi            "type": "ModifiedImmediate",
195*f5c631daSSadaf Ebrahimi          }
196*f5c631daSSadaf Ebrahimi        ]
197*f5c631daSSadaf Ebrahimi      }
198*f5c631daSSadaf Ebrahimi    ],
199*f5c631daSSadaf Ebrahimi    "inputs": [
200*f5c631daSSadaf Ebrahimi      {
201*f5c631daSSadaf Ebrahimi        "name": "apsr",
202*f5c631daSSadaf Ebrahimi        "type": "NZCV"
203*f5c631daSSadaf Ebrahimi      },
204*f5c631daSSadaf Ebrahimi      {
205*f5c631daSSadaf Ebrahimi        "name": "rd",
206*f5c631daSSadaf Ebrahimi        "type": "Register"
207*f5c631daSSadaf Ebrahimi      },
208*f5c631daSSadaf Ebrahimi      {
209*f5c631daSSadaf Ebrahimi        "name": "rn",
210*f5c631daSSadaf Ebrahimi        "type": "Register"
211*f5c631daSSadaf Ebrahimi      }
212*f5c631daSSadaf Ebrahimi    ]
213*f5c631daSSadaf Ebrahimi  ]
214*f5c631daSSadaf Ebrahimi  ~~~
215*f5c631daSSadaf Ebrahimi  """
216*f5c631daSSadaf Ebrahimi
217*f5c631daSSadaf Ebrahimi  operands = []
218*f5c631daSSadaf Ebrahimi  for json_operand in json_description["operands"]:
219*f5c631daSSadaf Ebrahimi    if "name" in json_operand and "type" in json_operand:
220*f5c631daSSadaf Ebrahimi      operands.append(data_type_builder.BuildOperand(json_operand["name"],
221*f5c631daSSadaf Ebrahimi                                                     json_operand["type"]))
222*f5c631daSSadaf Ebrahimi    elif "name" in json_operand and \
223*f5c631daSSadaf Ebrahimi         "wrapper" in json_operand and \
224*f5c631daSSadaf Ebrahimi         "operands" in json_operand:
225*f5c631daSSadaf Ebrahimi      wrapped_operands = [
226*f5c631daSSadaf Ebrahimi          data_type_builder.BuildOperand(json_wrapped_operand["name"],
227*f5c631daSSadaf Ebrahimi                                         json_wrapped_operand["type"])
228*f5c631daSSadaf Ebrahimi          for json_wrapped_operand in json_operand["operands"]
229*f5c631daSSadaf Ebrahimi      ]
230*f5c631daSSadaf Ebrahimi      operands.append(data_types.OperandWrapper(json_operand["name"],
231*f5c631daSSadaf Ebrahimi                                                json_operand["wrapper"],
232*f5c631daSSadaf Ebrahimi                                                wrapped_operands))
233*f5c631daSSadaf Ebrahimi    else:
234*f5c631daSSadaf Ebrahimi      raise Exception("Parser failed to recognize JSON \"description\".")
235*f5c631daSSadaf Ebrahimi  operand_list = generator.OperandList(operands)
236*f5c631daSSadaf Ebrahimi
237*f5c631daSSadaf Ebrahimi  json_description_inputs = json_description["inputs"]
238*f5c631daSSadaf Ebrahimi  input_list = generator.InputList([
239*f5c631daSSadaf Ebrahimi      data_type_builder.BuildInput(json_input["name"], json_input["type"])
240*f5c631daSSadaf Ebrahimi      for json_input in json_description_inputs
241*f5c631daSSadaf Ebrahimi  ])
242*f5c631daSSadaf Ebrahimi
243*f5c631daSSadaf Ebrahimi  return operand_list, input_list
244*f5c631daSSadaf Ebrahimi
245*f5c631daSSadaf Ebrahimi
246*f5c631daSSadaf Ebrahimidef ParseTestCase(json_test_case):
247*f5c631daSSadaf Ebrahimi  """
248*f5c631daSSadaf Ebrahimi  Build a `generator.TestCase` object from its JSON description.
249*f5c631daSSadaf Ebrahimi
250*f5c631daSSadaf Ebrahimi  ~~~
251*f5c631daSSadaf Ebrahimi  {
252*f5c631daSSadaf Ebrahimi    "name": "RdIsNotRn",
253*f5c631daSSadaf Ebrahimi    "operands": [
254*f5c631daSSadaf Ebrahimi      "rd", "rn"
255*f5c631daSSadaf Ebrahimi    ],
256*f5c631daSSadaf Ebrahimi    "inputs": [
257*f5c631daSSadaf Ebrahimi      "rd", "rn"
258*f5c631daSSadaf Ebrahimi    ],
259*f5c631daSSadaf Ebrahimi    "operand-filter": "rd != rn", // Python code to limit operand generation.
260*f5c631daSSadaf Ebrahimi    "operand-limit": 10           // Choose a random sample of 10 operands.
261*f5c631daSSadaf Ebrahimi  }
262*f5c631daSSadaf Ebrahimi  ...
263*f5c631daSSadaf Ebrahimi  {
264*f5c631daSSadaf Ebrahimi    "name": "Flags",
265*f5c631daSSadaf Ebrahimi    "operands": [
266*f5c631daSSadaf Ebrahimi      "cond"
267*f5c631daSSadaf Ebrahimi    ],
268*f5c631daSSadaf Ebrahimi    "inputs": [
269*f5c631daSSadaf Ebrahimi      "apsr", "q"
270*f5c631daSSadaf Ebrahimi    ],
271*f5c631daSSadaf Ebrahimi    "input-filter": "q == \"QFlag\"", // Python code to limit input generation
272*f5c631daSSadaf Ebrahimi    "input-limit": 200                // Choose a random sample of 200 inputs.
273*f5c631daSSadaf Ebrahimi  }
274*f5c631daSSadaf Ebrahimi  ...
275*f5c631daSSadaf Ebrahimi  {
276*f5c631daSSadaf Ebrahimi    "name": "InITBlock",
277*f5c631daSSadaf Ebrahimi    "operands": [
278*f5c631daSSadaf Ebrahimi      "cond", "rd", "rn", "rm"
279*f5c631daSSadaf Ebrahimi    ],
280*f5c631daSSadaf Ebrahimi    "in-it-block": "{cond}", // Generate an extra IT instruction. This string
281*f5c631daSSadaf Ebrahimi                             // will be used as the operand passed to IT. One
282*f5c631daSSadaf Ebrahimi                             // needs to specify under what name the condition
283*f5c631daSSadaf Ebrahimi                             // operand is represented, in braces.
284*f5c631daSSadaf Ebrahimi    "operand-filter": "cond != 'al' and rd == rm"
285*f5c631daSSadaf Ebrahimi  }
286*f5c631daSSadaf Ebrahimi  ~~~
287*f5c631daSSadaf Ebrahimi  """
288*f5c631daSSadaf Ebrahimi
289*f5c631daSSadaf Ebrahimi  # TODO: The fields in "operands" and "inputs" respectively refer to operands
290*f5c631daSSadaf Ebrahimi  # and inputs declared in the instruction description (see `ParseDescription`).
291*f5c631daSSadaf Ebrahimi  # We should assert that the user hasn't miss typed them and raise an
292*f5c631daSSadaf Ebrahimi  # exception.
293*f5c631daSSadaf Ebrahimi
294*f5c631daSSadaf Ebrahimi  # If the fields are not present, give them default values (empty list,
295*f5c631daSSadaf Ebrahimi  # "True", or "None").
296*f5c631daSSadaf Ebrahimi  operand_names = json_test_case["operands"] \
297*f5c631daSSadaf Ebrahimi      if "operands" in json_test_case else []
298*f5c631daSSadaf Ebrahimi  input_names = json_test_case["inputs"] if "inputs" in json_test_case else []
299*f5c631daSSadaf Ebrahimi  operand_filter = json_test_case["operand-filter"] \
300*f5c631daSSadaf Ebrahimi      if "operand-filter" in json_test_case else "True"
301*f5c631daSSadaf Ebrahimi  input_filter = json_test_case["input-filter"] \
302*f5c631daSSadaf Ebrahimi      if "input-filter" in json_test_case else "True"
303*f5c631daSSadaf Ebrahimi  operand_limit = json_test_case["operand-limit"] \
304*f5c631daSSadaf Ebrahimi      if "operand-limit" in json_test_case else None
305*f5c631daSSadaf Ebrahimi  input_limit = json_test_case["input-limit"] \
306*f5c631daSSadaf Ebrahimi      if "input-limit" in json_test_case else None
307*f5c631daSSadaf Ebrahimi  in_it_block = json_test_case["in-it-block"] \
308*f5c631daSSadaf Ebrahimi      if "in-it-block" in json_test_case else None
309*f5c631daSSadaf Ebrahimi
310*f5c631daSSadaf Ebrahimi  # Create a seed from the test case description. It will only change if the
311*f5c631daSSadaf Ebrahimi  # test case has changed.
312*f5c631daSSadaf Ebrahimi  md5 = hashlib.md5(str(json_test_case).encode())
313*f5c631daSSadaf Ebrahimi  seed = md5.hexdigest()
314*f5c631daSSadaf Ebrahimi
315*f5c631daSSadaf Ebrahimi  return generator.TestCase(json_test_case["name"], seed, operand_names, input_names,
316*f5c631daSSadaf Ebrahimi                            operand_filter, input_filter, operand_limit,
317*f5c631daSSadaf Ebrahimi                            input_limit, in_it_block)
318*f5c631daSSadaf Ebrahimi
319*f5c631daSSadaf Ebrahimi
320*f5c631daSSadaf Ebrahimidef ParseTestFile(test_name, test_isa, mnemonics, operand_list, input_list,
321*f5c631daSSadaf Ebrahimi                  json_test_file):
322*f5c631daSSadaf Ebrahimi  """
323*f5c631daSSadaf Ebrahimi  Build a `generator.Generator` object from a test file description. We have one
324*f5c631daSSadaf Ebrahimi  for each generated test files.
325*f5c631daSSadaf Ebrahimi
326*f5c631daSSadaf Ebrahimi  ~~~
327*f5c631daSSadaf Ebrahimi  {
328*f5c631daSSadaf Ebrahimi    "type": "simulator",  // Type of the test. This will control the prefix we
329*f5c631daSSadaf Ebrahimi                          // use when naming the file to generate.
330*f5c631daSSadaf Ebrahimi    "name": "special-case",  // Optional name that will be included in the
331*f5c631daSSadaf Ebrahimi                             // generated filename.
332*f5c631daSSadaf Ebrahimi    "mnemonics": [  // Optional list of instruction, overriding the top-level
333*f5c631daSSadaf Ebrahimi      "Adc",        // one.
334*f5c631daSSadaf Ebrahimi      "Add",
335*f5c631daSSadaf Ebrahimi      ...
336*f5c631daSSadaf Ebrahimi    ],
337*f5c631daSSadaf Ebrahimi    "test-cases": [
338*f5c631daSSadaf Ebrahimi      ... // Test case descriptions parsed with `ParseTestCase`.
339*f5c631daSSadaf Ebrahimi    ]
340*f5c631daSSadaf Ebrahimi  }
341*f5c631daSSadaf Ebrahimi  ~~~
342*f5c631daSSadaf Ebrahimi  """
343*f5c631daSSadaf Ebrahimi  name = json_test_file["name"] if "name" in json_test_file else ""
344*f5c631daSSadaf Ebrahimi  if name is not "":
345*f5c631daSSadaf Ebrahimi    test_name = test_name + "-" + name
346*f5c631daSSadaf Ebrahimi  # Override the top-level mnemonics list with a subset.
347*f5c631daSSadaf Ebrahimi  if "mnemonics" in json_test_file:
348*f5c631daSSadaf Ebrahimi    if set(json_test_file["mnemonics"]) == set(mnemonics):
349*f5c631daSSadaf Ebrahimi      raise Exception(
350*f5c631daSSadaf Ebrahimi          "Overriding mnemonic list is identical to the top-level list")
351*f5c631daSSadaf Ebrahimi    if not(set(json_test_file["mnemonics"]) < set(mnemonics)):
352*f5c631daSSadaf Ebrahimi      raise Exception(
353*f5c631daSSadaf Ebrahimi          "Overriding mnemonic list should a subset of the top-level list")
354*f5c631daSSadaf Ebrahimi    mnemonics = json_test_file["mnemonics"]
355*f5c631daSSadaf Ebrahimi  test_cases = [
356*f5c631daSSadaf Ebrahimi      ParseTestCase(json_test_case)
357*f5c631daSSadaf Ebrahimi      for json_test_case in json_test_file["test-cases"]
358*f5c631daSSadaf Ebrahimi  ]
359*f5c631daSSadaf Ebrahimi  return generator.Generator(test_name, test_isa, json_test_file["type"],
360*f5c631daSSadaf Ebrahimi                             mnemonics, operand_list, input_list, test_cases)
361*f5c631daSSadaf Ebrahimi
362*f5c631daSSadaf Ebrahimi
363*f5c631daSSadaf Ebrahimidef ParseConfig(test_name, test_isas, data_type_builder, json_config):
364*f5c631daSSadaf Ebrahimi  """
365*f5c631daSSadaf Ebrahimi  Return a list of `generator.Generator` objects from a JSON description. This
366*f5c631daSSadaf Ebrahimi  is the top-level description.
367*f5c631daSSadaf Ebrahimi
368*f5c631daSSadaf Ebrahimi  ~~~
369*f5c631daSSadaf Ebrahimi  {
370*f5c631daSSadaf Ebrahimi    "mnemonics": [
371*f5c631daSSadaf Ebrahimi      "Adc",
372*f5c631daSSadaf Ebrahimi      "Add",
373*f5c631daSSadaf Ebrahimi      ...
374*f5c631daSSadaf Ebrahimi    ],
375*f5c631daSSadaf Ebrahimi    "description": [
376*f5c631daSSadaf Ebrahimi      ... // Instruction description parsed with `ParseDescription`.
377*f5c631daSSadaf Ebrahimi    ],
378*f5c631daSSadaf Ebrahimi    "test-files": [
379*f5c631daSSadaf Ebrahimi      ... // Test files descriptions parsed with `ParseTestFile`.
380*f5c631daSSadaf Ebrahimi    ]
381*f5c631daSSadaf Ebrahimi  }
382*f5c631daSSadaf Ebrahimi  ~~~
383*f5c631daSSadaf Ebrahimi  """
384*f5c631daSSadaf Ebrahimi  mnemonics = json_config["mnemonics"]
385*f5c631daSSadaf Ebrahimi  operand_list, input_list = ParseDescription(
386*f5c631daSSadaf Ebrahimi      data_type_builder, json_config["description"])
387*f5c631daSSadaf Ebrahimi
388*f5c631daSSadaf Ebrahimi  return itertools.chain(*[[
389*f5c631daSSadaf Ebrahimi          ParseTestFile(test_name, test_isa, mnemonics, operand_list,
390*f5c631daSSadaf Ebrahimi                        input_list, json_test_file)
391*f5c631daSSadaf Ebrahimi          for json_test_file in json_config["test-files"]
392*f5c631daSSadaf Ebrahimi      ]
393*f5c631daSSadaf Ebrahimi      for test_isa in test_isas
394*f5c631daSSadaf Ebrahimi  ])
395*f5c631daSSadaf Ebrahimi
396*f5c631daSSadaf Ebrahimi
397*f5c631daSSadaf Ebrahimidef GetTestNameAndISAFromFileName(filename):
398*f5c631daSSadaf Ebrahimi  """
399*f5c631daSSadaf Ebrahimi  Return a tuple (name, [isa, ...]) extracted from the file name.
400*f5c631daSSadaf Ebrahimi  """
401*f5c631daSSadaf Ebrahimi  # Strip the ".json" extension
402*f5c631daSSadaf Ebrahimi  stripped_basename = os.path.splitext(os.path.basename(filename))[0]
403*f5c631daSSadaf Ebrahimi  # The ISA is the last element in the filename, seperated with "-".
404*f5c631daSSadaf Ebrahimi  if stripped_basename.endswith(('-a32', '-t32')):
405*f5c631daSSadaf Ebrahimi    isa = [stripped_basename[-3:]]
406*f5c631daSSadaf Ebrahimi    test_name = stripped_basename[:-4]
407*f5c631daSSadaf Ebrahimi  else:
408*f5c631daSSadaf Ebrahimi    # If the ISA is ommitted, support both.
409*f5c631daSSadaf Ebrahimi    isa = ["a32", "t32"]
410*f5c631daSSadaf Ebrahimi    test_name = stripped_basename
411*f5c631daSSadaf Ebrahimi
412*f5c631daSSadaf Ebrahimi  return (test_name, isa)
413*f5c631daSSadaf Ebrahimi
414*f5c631daSSadaf Ebrahimi
415*f5c631daSSadaf Ebrahimidef GetTestNameFromFileName(filename):
416*f5c631daSSadaf Ebrahimi  """
417*f5c631daSSadaf Ebrahimi  Return the name given to this test from its file name, stripped of the
418*f5c631daSSadaf Ebrahimi  optional "a32" or "t32" at the end.
419*f5c631daSSadaf Ebrahimi  """
420*f5c631daSSadaf Ebrahimi  test_name, _ = GetTestNameAndISAFromFileName(filename)
421*f5c631daSSadaf Ebrahimi  return test_name
422*f5c631daSSadaf Ebrahimi
423*f5c631daSSadaf Ebrahimi
424*f5c631daSSadaf Ebrahimidef GetISAsFromFileName(filename):
425*f5c631daSSadaf Ebrahimi  """
426*f5c631daSSadaf Ebrahimi  Return a list of ISAs supported by the test, from the file name, either
427*f5c631daSSadaf Ebrahimi  ["a32"], ["t32"] or both.
428*f5c631daSSadaf Ebrahimi  """
429*f5c631daSSadaf Ebrahimi  _, isas = GetTestNameAndISAFromFileName(filename)
430*f5c631daSSadaf Ebrahimi
431*f5c631daSSadaf Ebrahimi  return isas
432*f5c631daSSadaf Ebrahimi
433*f5c631daSSadaf Ebrahimidef Parse(data_type_file, config_files):
434*f5c631daSSadaf Ebrahimi  """
435*f5c631daSSadaf Ebrahimi  Parse the `data_type_file` and `test_case_files` json description files into a
436*f5c631daSSadaf Ebrahimi  list of (name, test_case) tuples. Test cases are `generator.TestCase`
437*f5c631daSSadaf Ebrahimi  objects that can be used to generate C++.
438*f5c631daSSadaf Ebrahimi  """
439*f5c631daSSadaf Ebrahimi
440*f5c631daSSadaf Ebrahimi  # Create a `DataTypeBuilder` object. This object will passed down and used to
441*f5c631daSSadaf Ebrahimi  # instantiate `data_types.Operand` and `data_types.Input` objects.
442*f5c631daSSadaf Ebrahimi  data_type_builder = ParseDataTypes(LoadJSON(data_type_file))
443*f5c631daSSadaf Ebrahimi
444*f5c631daSSadaf Ebrahimi  # Build a list of (name, JSON) tuples to represent the new tests.
445*f5c631daSSadaf Ebrahimi  json_configs = [
446*f5c631daSSadaf Ebrahimi      # We create the name of the test by looking at the file name stripped of
447*f5c631daSSadaf Ebrahimi      # its extension.
448*f5c631daSSadaf Ebrahimi      (GetTestNameFromFileName(config_file), GetISAsFromFileName(config_file),
449*f5c631daSSadaf Ebrahimi       LoadJSON(config_file))
450*f5c631daSSadaf Ebrahimi      for config_file in config_files
451*f5c631daSSadaf Ebrahimi  ]
452*f5c631daSSadaf Ebrahimi
453*f5c631daSSadaf Ebrahimi  # Return a list of Generator objects. The generator is the entry point to
454*f5c631daSSadaf Ebrahimi  # generate a file.
455*f5c631daSSadaf Ebrahimi  # Note that `ParseConfig` returns a list of generators already. We use `chain`
456*f5c631daSSadaf Ebrahimi  # here to flatten a list of lists into just a list.
457*f5c631daSSadaf Ebrahimi  return itertools.chain(*[
458*f5c631daSSadaf Ebrahimi      ParseConfig(test_name, test_isas, data_type_builder, json_config)
459*f5c631daSSadaf Ebrahimi      for test_name, test_isas, json_config in json_configs
460*f5c631daSSadaf Ebrahimi  ])
461