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