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