1 #!/usr/bin/env python3 2 3 # Copyright 2016, VIXL authors 4 # All rights reserved. 5 # 6 # Redistribution and use in source and binary forms, with or without 7 # modification, are permitted provided that the following conditions are met: 8 # 9 # * Redistributions of source code must retain the above copyright notice, 10 # this list of conditions and the following disclaimer. 11 # * Redistributions in binary form must reproduce the above copyright notice, 12 # this list of conditions and the following disclaimer in the documentation 13 # and/or other materials provided with the distribution. 14 # * Neither the name of ARM Limited nor the names of its contributors may be 15 # used to endorse or promote products derived from this software without 16 # specific prior written permission. 17 # 18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND 19 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 """ 30 Generating tests 31 ================ 32 33 From the VIXL toplevel directory run: 34 35 $ ./tools/generate_tests.py 36 37 The script assumes that `clang-format-3.6` is in the current path. If it isn't, 38 you can provide your own: 39 40 $ ./tools/generate_tests.py --clang-format /patch/to/clang-format 41 42 Once the script has finished, it will have generated test files, as many as 43 present in the `default_config_files` list. For example: 44 45 - test/aarch32/test-assembler-cond-rd-rn-immediate-a32.cc 46 - test/aarch32/test-assembler-cond-rd-rn-rm-a32.cc 47 - test/aarch32/test-assembler-cond-rd-rn-rm-q-a32.cc 48 - test/aarch32/test-assembler-cond-rd-rn-rm-ge-a32.cc 49 50 Because these test cases need traces in order to build, the script will have 51 generated dummy trace files in `test/aarch32/traces/`. If you look at them 52 you'll see they are basically empty: 53 54 $ cat test/aarch32/traces/sim-cond-rd-rn-immediate-adc-a32.h 55 static const TestResult *kReferenceAdc = NULL; 56 57 So of course, we can now build the test cases but running them will crash. We 58 need to re-generate traces with real hardware; the test cases do not support 59 running in the simulator just yet. 60 61 Generating traces 62 ================= 63 64 You need to have either compiled natively for ARM, or cross-compiled 65 `test-runner`. The traces can then be generated in the same way as with VIXL64. 66 Note that it takes a few minutes to generate everything. 67 68 ./tools/generate_simulator_traces.py --runner /path/to/test-runner \ 69 --aarch32-only 70 71 You can now rebuild everything. If it all goes well, running the new tests 72 should pass. 73 74 Test configuration format 75 ========================= 76 77 TODO: Write a simple and well documented complete example configuration file and 78 mention it here. 79 80 The underlying `test_generator` framework reads JSON description files and 81 generates tests according to them. These files live in `test/aarch32/config` by 82 default, but you may provide your own files with the `--config-files FILE ...` 83 flag. The JSON format was extended to support C++ like one-line comments. 84 85 Each configuration file will serve to generate one or more test files, 86 we even use its file name to choose the name of the test: 87 88 test/aarch32/config/cond-rd-rn-immediate-a32.json 89 `-> test/aarch32/test-simulator-cond-rd-rn-immediate-a32.cc 90 `-> test/aarch32/test-assembler-cond-rd-rn-immediate-a32.cc 91 92 In addition to these test configuration files, we also provide a JSON 93 description with shared information. This information represents data types that 94 instructions use and lives in `test/aarch32/config/data-types.json`. 95 96 Data types description 97 ---------------------- 98 99 We refer to two kinds of data types: `operand` and `input`. 100 101 An `operand` represents an argument passed to the macro-assembler to generate an 102 instruction. For example, a register or an immediate are operands. We can think 103 of it as "assemble-time" data. 104 105 As opposed to `operands`, an `input` represents data passed to an instruction at 106 runtime. For example, it will be the value you write to a register before 107 executing the instruction under test. 108 109 The `data-types.json` file has the following structure: 110 111 ~~~ 112 { 113 "operands": [ 114 // List of operand types. 115 ], 116 "inputs": [ 117 // List of input types. 118 ] 119 } 120 ~~~ 121 122 Each operand is described with the following structure: 123 124 ~~~ 125 { 126 // Unique name for this operand type. 127 "name": "AllRegistersButPC", 128 // C++ type used by VIXL to represent this operand. 129 "type": "Register", 130 // List of possible variants. 131 "variants": [ 132 "r0", 133 "r1", 134 "r2", 135 "r3", 136 "r4", 137 "r5", 138 "r6", 139 "r7", 140 "r8", 141 "r9", 142 "r10", 143 "r11", 144 "r12", 145 "r13", 146 "r14" 147 ], 148 // Default variant to use. 149 "default": "r0" 150 } 151 ~~~ 152 153 The "name" field of the operand will be used by test configuration files in 154 order to specify what kind of operands an instruction takes. The "type" field 155 simply tells the generator what C++ type should be generated, e.g. "Condition", 156 "Register", "uint32_t", "ShiftType", ...etc. 157 158 Inputs are described in a very similar way: 159 160 ~~~ 161 { 162 // Unique name for this input type. 163 "name": "Register", 164 // Python type from `test_generator.data_types` to use to generate C++ code 165 // for this input. 166 "type": "Register", 167 // List of possible values. 168 "values": [ 169 "0x00000000", 170 "0xffffffff", 171 "0xabababab", 172 "0x5a5a5a5a" 173 ], 174 // Default value. 175 "default": "0xabababab" 176 } 177 ~~~ 178 179 The "name" field has the same purpose as for operands. The "type" field however, 180 is the name of a Python class in `test_generator.data_types`. The type will 181 specify what C++ code to generate in order to load and record the input value, 182 e.g. how to load a value into a register, how to read and record it. 183 184 When adding more tests, one may have to create new data types in this file. For 185 example, when we want to test an instruction with a different set of registers. 186 If adding new input types which need different C++ code to load and record them, 187 one will have to add it to `test_generator.data_types` and override the 188 `Epilogue` and `Prologue` methods. 189 190 Test configuration 191 ------------------ 192 193 Once we have all the data types we need described, we need test configuration 194 files to describe what instructions to test and with what `inputs` and 195 `operands` they take. 196 197 These files have the following structure: 198 199 ~~~ 200 { 201 "mnemonics": [ 202 // List of instruction mnemonics to use. These must correspond to 203 // `MacroAssembler` methods. 204 ], 205 "description": { 206 "operands": [ 207 // List of operands the instruction takes. 208 ], 209 "inputs: [ 210 // List of inputs the instruction can be affected by. 211 ] 212 }, 213 // List of files to generate. 214 "test-files": [ 215 { 216 "type": "assembler", 217 "mnemonics": [ 218 // Optional list of instruction mnemonics to use, overriding the 219 // top-level list. 220 ], 221 "test-cases": [ 222 // List of test cases for "assembler" tests, see below for 223 // details. 224 ] 225 }, 226 { 227 "type": "simulator", 228 "test-cases": [ 229 // List of test cases for "simulator" tests, see below for 230 // details. 231 ] 232 } 233 ] 234 } 235 ~~~ 236 237 - List of operands: 238 239 The operand list describes the actual argument to the `MacroAssembler` method. 240 For example, if we take instruction in the form 241 "XXX.cond rd rn rm shift #amount": 242 243 We want to generate C++ code as such: 244 245 ~~~ 246 Condition cond = ...; 247 Register rd = ...; 248 Register rn = ...; 249 Register rm = ...; 250 ShiftType type = ...; 251 uint32_t amount = ...; 252 Operand op(rm, type, amount); 253 254 __ Xxx(cond, rd, rn, op); 255 ~~~ 256 257 We will have the following operand list: 258 259 ~~~ 260 "operands": [ 261 { 262 "name": "cond", 263 "type": "Condition" 264 }, 265 { 266 "name": "rd", 267 "type": "AllRegistersButPC" 268 }, 269 { 270 "name": "rn", 271 "type": "AllRegistersButPC" 272 }, 273 { 274 "name": "op", 275 "wrapper": "Operand", 276 "operands": [ 277 { 278 "name": "rm", 279 "operand": "AllRegistersButPC" 280 }, 281 { 282 "name": "type", 283 "operand": "Shift" 284 }, 285 { 286 "name": "amount", 287 "operand": "ImmediateShiftAmount" 288 } 289 ] 290 } 291 ] 292 ~~~ 293 294 The "name" field represents the identifier of the operand and will be used as a 295 variable name in the generated code. The "type" field corresponds to an operand 296 type described in the `data-types.json` file as described above. 297 298 We can see that we've wrapped the last three operands into an "op" 299 wrapper object. This allows us to tell the generator to wrap these 300 operands into a `Operand` C++ object. 301 302 - List of inputs: 303 304 This structure is similar to the operand list, but this time it describes what 305 input data the instructions may be affected by at runtime. If we take the same 306 example as above, we will have the following list: 307 308 ~~~ 309 "inputs": [ 310 { 311 "name": "apsr", 312 "type": "NZCV" 313 }, 314 { 315 "name": "rd", 316 "type": "Register" 317 }, 318 { 319 "name": "rn", 320 "type": "Register" 321 }, 322 { 323 "name": "rm", 324 "type": "Register" 325 } 326 ] 327 ~~~ 328 329 This will specify what C++ code to generate before and after emitting the 330 instruction under test. The C++ code will set and record register values for 331 example. See `test_generator.data_types` for more details. 332 333 - Test files and test cases: 334 335 Up until now, we've only just described the environment in which instructions 336 can operate. We need to express what files we want generating, what instructions 337 we want to test and what we want them to do. 338 339 As previously mentioned, a configuration file can control the generation of 340 several test files. We will generate one file per element in the "test-files" 341 array: 342 343 ~~~ 344 "test-files": [ 345 { 346 "type": "assembler", 347 "test-cases": [ 348 // List of test cases for "assembler" tests, see below for 349 // details. 350 ] 351 }, 352 { 353 "type": "assembler", 354 "name": "special-case", 355 "mnemonics": [ 356 // Override the top-level list with a subset of instructions concerned 357 // with this special case. 358 ], 359 "test-cases": [ 360 // List of test cases for "assembler" tests, see below for 361 // details. 362 ] 363 }, 364 { 365 "type": "simulator", 366 "test-cases": [ 367 // List of test cases for "simulator" tests, see below for 368 // details. 369 ] 370 } 371 ] 372 ~~~ 373 374 Above, we've decided to generate three tests: a "simulator" test and two 375 "assembler" tests. The resulting files will have names with the following 376 pattern. 377 378 - "test/aarch32/test-assembler-{configuration name}-a32.cc" 379 - "test/aarch32/test-assembler-{configuration name}-special-case-a32.cc" 380 - "test/aarch32/test-simulator-{configuration name}-a32.cc" 381 382 The "type" field describes the kind of testing we want to do, these types are 383 recognized by the generator and, at the moment, can be one of "simulator", 384 "assembler" and "macro-assembler". Simulator tests will run each instruction and 385 record the changes while assembler tests will only record the code buffer and 386 never execute anything. MacroAssembler tests currently only generate code to 387 check that the MacroAssembler does not crash; the output itself is not yet 388 tested. Because you may want to generate more than one test of the same type, as 389 we are doing in the example, we need a way to differentiate them. You may use 390 the optional "name" field for this. 391 392 Finally, we describe how to test the instruction by declaring a list of test 393 cases with the "test-cases" field. 394 395 Here is an example of what we can express: 396 ~~~ 397 [ 398 // Generate all combinations of instructions where "rd" an "rn" are the same 399 // register and "cond" and "rm" are just the default. 400 // For example: 401 // __ Xxx(al, r0, r0, r0); 402 // __ Xxx(al, r1, r1, r0); 403 // __ Xxx(al, r2, r2, r0); 404 // ... 405 // __ Xxx(al, r12, r12, r0); 406 // __ Xxx(al, r13, r13, r0); 407 // __ Xxx(al, r14, r14, r0); 408 // 409 // For each of the instructions above, run them with a different value in "rd" 410 // and "rn". 411 { 412 "name": "RdIsRn", 413 "operands": [ 414 "rd", "rn" 415 ], 416 "operand-filter": "rd == rn", 417 "inputs": [ 418 "rd", "rn" 419 ], 420 "input-filter": "rd == rn" 421 }, 422 // Generate all combinations of instructions with different condition codes. 423 // For example: 424 // __ Xxx(eq, r0, r0, r0); 425 // __ Xxx(ne, r0, r0, r0); 426 // __ Xxx(cs, r0, r0, r0); 427 // ... 428 // __ Xxx(gt, r0, r0, r0); 429 // __ Xxx(le, r0, r0, r0); 430 // __ Xxx(al, r0, r0, r0); 431 // 432 // For each of the instructions above, run them against all combinations of 433 // NZCV bits. 434 { 435 "name": "ConditionVersusNZCV", 436 "operands": [ 437 "cond" 438 ], 439 "inputs": [ 440 "apsr" 441 ] 442 }, 443 // We are interested in testing that the Q bit gets set and cleared, so we've 444 // limited the instruction generation to a single instruction and instead have 445 // stressed the values put in "rn" and "rm". 446 // 447 // So for this instruction, we choose to run it will all combinations of 448 // values in "rn" and "rm". Additionally, we include "qbit" in the inputs, 449 // which will make the test set or clear it before executing the instruction. 450 // Note that "qbit" needs to be declared as an input in the instruction 451 // description (see "List of inputs" section). 452 { 453 "name": "Qbit", 454 "operands": [ 455 "rn", "rm" 456 ], 457 "inputs": [ 458 "qbit", "rn", "rm" 459 ], 460 "operand-filter": "rn != rm'", 461 "operand-limit": 1 462 }, 463 // Generate 10 random instructions with all different registers but use the 464 // default condition. 465 // For example: 466 // __ Xxx(al, r5, r1, r0); 467 // __ Xxx(al, r8, r9, r7); 468 // __ Xxx(al, r9, r1, r2); 469 // __ Xxx(al, r0, r6, r2); 470 // __ Xxx(al, r11, r9, r11); 471 // __ Xxx(al, r14, r2, r11); 472 // __ Xxx(al, r8, r2, r5); 473 // __ Xxx(al, r10, r0, r1); 474 // __ Xxx(al, r11, r2, r7); 475 // __ Xxx(al, r2, r6, r1); 476 // 477 // For each instruction, feed it 200 different combination of values in the 478 // three registers. 479 { 480 "name": "RegisterSimulatorTest", 481 "operands": [ 482 "rd", "rn", "rm" 483 ], 484 "inputs": [ 485 "rd", "rn", "rm" 486 ], 487 "operand-limit": 10, 488 "input-limit": 200 489 } 490 ] 491 ~~~ 492 493 Assembler test cases are much simpler, here are some examples: 494 ~~~ 495 // Generate 2000 random instructions out of all possible operand combinations. 496 { 497 "name": "LotsOfRandomInstructions", 498 "operands": [ 499 "cond", "rd", "rn", "rm" 500 ], 501 "operand-limit": 2000 502 }, 503 // Same as above but limit the test to 200 instructions where rd == rn. 504 { 505 "name": "RdIsRn", 506 "operands": [ 507 "cond", "rd", "rn", "rm" 508 ], 509 "operand-filter": "rd == rn", 510 "operand-limit": 200 511 } 512 ~~~ 513 514 As can be expected, assembler test do not have the notion of "inputs". 515 516 Here are details about each field. Note that all of them except for "name" are 517 optional. 518 519 * "name": 520 521 A unique name should be given to the test case, it will be used to give the 522 generated C++ `const Input[]` array a name. 523 524 * "operands": 525 526 List of operand names that we are interested in testing. The generator will 527 lookup the list of variants for each operand and build the product of all of 528 them. It will then choose the default variant for the operands not specified 529 here. 530 531 * "operand-filter": 532 533 As you would expect, the product of all operand variants may be huge. To 534 prevent this, you may specify a Python expression to filter the list. 535 536 * "operand-limit": 537 538 We can potentially obtain a *massive* set of variants of instructions, as we 539 are computing a product of operand variants in "operands". This field allows 540 us to limit this by choosing a random sample from the computed variants. 541 Note that this is a seeded pseudo-random sample, and the seed corresponds to 542 the test case description. The same test case description will always 543 generate the same code. 544 545 * "inputs": 546 547 This is exactly the same as "operands" but for inputs. 548 549 * "input-filter": 550 551 Ditto. 552 553 * "input-limit": 554 555 Ditto. 556 557 Here is an example of the C++ code that will be generated for a given test case. 558 For simplicity, let's generate tests for an instruction with only `NZCV` and two 559 registers as inputs. 560 561 For the following test case, which will target encodings where `rd` and `rn` are 562 the same registers: 563 564 ~~~ 565 { 566 "name": "RdIsRn", 567 "operands": [ 568 "rd", "rn" 569 ], 570 "operand-filter": "rd == rn", 571 "inputs": [ 572 "rd", "rn" 573 ], 574 "input-filter": "rd == rn" 575 }, 576 ~~~ 577 578 It will generate the following input array. 579 580 ~~~ 581 // apsr, rd, rn 582 static const Inputs kRdIsRn[] = {{NoFlag, 0x00000000, 0x00000000}, 583 {NoFlag, 0xffffffff, 0xffffffff}, 584 {NoFlag, 0xabababab, 0xabababab}, 585 {NoFlag, 0x5a5a5a5a, 0x5a5a5a5a}}; 586 ~~~ 587 588 We can see that the default apsr value was chosen (NoFlag), as apsr is not in 589 the list of "inputs". 590 591 It will also generate a list of instructions to test: 592 593 ~~~ 594 static const TestLoopData kTests[] = { 595 {{al, r1, r1, 0x000000ab}, ARRAY_SIZE(kRdIsRn), kRdIsRn, "RdIsRn"}, 596 {{al, r2, r2, 0x000000ab}, ARRAY_SIZE(kRdIsRn), kRdIsRn, "RdIsRn"}, 597 {{al, r8, r8, 0x000000ab}, ARRAY_SIZE(kRdIsRn), kRdIsRn, "RdIsRn"}, 598 {{al, r9, r9, 0x000000ab}, ARRAY_SIZE(kRdIsRn), kRdIsRn, "RdIsRn"}, 599 }; 600 ~~~ 601 602 As a result, the new test we will assemble each instructions in "mnemonics" with 603 all of the operands described in `kTests` above. And each instruction will be 604 executed and passed all inputs in `kRdIsRn`. 605 """ 606 607 import subprocess 608 import argparse 609 import string 610 import re 611 import multiprocessing 612 import functools 613 614 import test_generator.parser 615 616 617 default_config_files = [ 618 'test/aarch32/config/rd-rn-rm-a32.json', 619 'test/aarch32/config/cond-rd-rn-operand-const-a32.json', 620 'test/aarch32/config/cond-rd-rn-operand-rm-a32.json', 621 'test/aarch32/config/cond-rd-rn-operand-rm-shift-amount-1to31-a32.json', 622 'test/aarch32/config/cond-rd-rn-operand-rm-shift-amount-1to32-a32.json', 623 'test/aarch32/config/cond-rd-rn-operand-rm-shift-rs-a32.json', 624 'test/aarch32/config/cond-rd-rn-operand-rm-ror-amount-a32.json', 625 'test/aarch32/config/cond-rd-rn-a32.json', 626 'test/aarch32/config/cond-rd-rn-pc-a32.json', 627 'test/aarch32/config/cond-rd-rn-rm-a32.json', 628 'test/aarch32/config/cond-rd-operand-const-a32.json', 629 'test/aarch32/config/cond-rd-operand-rn-a32.json', 630 'test/aarch32/config/cond-rd-operand-rn-shift-amount-1to31-a32.json', 631 'test/aarch32/config/cond-rd-operand-rn-shift-amount-1to32-a32.json', 632 'test/aarch32/config/cond-rd-operand-rn-shift-rs-a32.json', 633 'test/aarch32/config/cond-rd-operand-rn-ror-amount-a32.json', 634 'test/aarch32/config/cond-rd-memop-immediate-512-a32.json', 635 'test/aarch32/config/cond-rd-memop-immediate-8192-a32.json', 636 'test/aarch32/config/cond-rd-memop-rs-a32.json', 637 'test/aarch32/config/cond-rd-memop-rs-shift-amount-1to31-a32.json', 638 'test/aarch32/config/cond-rd-memop-rs-shift-amount-1to32-a32.json', 639 640 'test/aarch32/config/cond-rd-rn-t32.json', 641 'test/aarch32/config/cond-rd-rn-rm-t32.json', 642 'test/aarch32/config/cond-rdlow-rnlow-rmlow-t32.json', 643 'test/aarch32/config/cond-rd-rn-operand-const-t32.json', 644 'test/aarch32/config/cond-rd-pc-operand-imm12-t32.json', 645 'test/aarch32/config/cond-rd-rn-operand-imm12-t32.json', 646 'test/aarch32/config/cond-rd-pc-operand-imm8-t32.json', 647 'test/aarch32/config/cond-rd-sp-operand-imm8-t32.json', 648 'test/aarch32/config/cond-rdlow-rnlow-operand-immediate-t32.json', 649 'test/aarch32/config/cond-sp-sp-operand-imm7-t32.json', 650 'test/aarch32/config/cond-rd-rn-operand-rm-t32.json', 651 'test/aarch32/config/cond-rd-rn-operand-rm-shift-amount-1to31-t32.json', 652 'test/aarch32/config/cond-rd-rn-operand-rm-shift-amount-1to32-t32.json', 653 'test/aarch32/config/cond-rd-rn-operand-rm-ror-amount-t32.json', 654 'test/aarch32/config/cond-rd-operand-const-t32.json', 655 'test/aarch32/config/cond-rd-operand-imm16-t32.json', 656 'test/aarch32/config/cond-rdlow-operand-imm8-t32.json', 657 'test/aarch32/config/cond-rd-operand-rn-shift-amount-1to31-t32.json', 658 'test/aarch32/config/cond-rd-operand-rn-shift-amount-1to32-t32.json', 659 'test/aarch32/config/cond-rd-operand-rn-shift-rs-t32.json', 660 'test/aarch32/config/cond-rd-operand-rn-ror-amount-t32.json', 661 'test/aarch32/config/cond-rd-operand-rn-t32.json', 662 'test/aarch32/config/rd-rn-rm-t32.json', 663 ] 664 665 666 # Link a test type with a template file. 667 template_files = { 668 'simulator': "test/aarch32/config/template-simulator-aarch32.cc.in", 669 'assembler': "test/aarch32/config/template-assembler-aarch32.cc.in", 670 'macro-assembler': "test/aarch32/config/template-macro-assembler-aarch32.cc.in", 671 } 672 673 674 def BuildOptions(): 675 result = argparse.ArgumentParser( 676 description = 'Test generator for AArch32.', 677 formatter_class=argparse.ArgumentDefaultsHelpFormatter) 678 result.add_argument('--config-files', nargs='+', 679 default=default_config_files, 680 metavar='FILE', 681 help='Configuration files, each will generate a test file.') 682 result.add_argument('--clang-format', 683 default='clang-format-3.6', help='Path to clang-format.') 684 result.add_argument('--jobs', '-j', type=int, metavar='N', 685 default=multiprocessing.cpu_count(), 686 help='Allow N jobs at once') 687 result.add_argument('--skip-traces', action='store_true', 688 help='Skip generation of dummy traces.') 689 return result.parse_args() 690 691 692 def DoNotEditComment(template_file): 693 # We rely on `clang-format` to wrap this comment to 80 characters. 694 return """ 695 // ----------------------------------------------------------------------------- 696 // This file is auto generated from the {} template file using tools/generate_tests.py. 697 // 698 // PLEASE DO NOT EDIT. 699 // ----------------------------------------------------------------------------- 700 """.format(template_file) 701 702 def GenerateTest(generator, clang_format, skip_traces): 703 template_file = template_files[generator.test_type] 704 generated_file = "" 705 with open(template_file, "r") as f: 706 # Strip out comments starting with three forward slashes before creating the 707 # string.Template object. 708 template = string.Template(re.sub("\/\/\/.*", "", f.read())) 709 710 # The `generator` object has methods generating strings to fill the template. 711 generated_file = template.substitute({ 712 # Add a top comment stating this file is auto-generated. 713 'do_not_edit_comment': DoNotEditComment(template_file), 714 715 # List of mnemonics. 716 'instruction_list_declaration': generator.InstructionListDeclaration(), 717 718 # Declarations. 719 'operand_list_declaration': generator.OperandDeclarations(), 720 'input_declarations': generator.InputDeclarations(), 721 722 # Definitions. 723 'input_definitions': generator.InputDefinitions(), 724 'test_case_definitions': generator.TestCaseDefinitions(), 725 726 # Include traces. 727 'include_trace_files': generator.IncludeTraceFiles(), 728 729 # Define a typedef for the MacroAssembler method. 730 'macroassembler_method_args': generator.MacroAssemblerMethodArgs(), 731 732 # Generate code to switch instruction set. 733 'macroassembler_set_isa': generator.MacroAssemblerSetISA(), 734 735 # Generate code to emit instructions. 736 'code_instantiate_operands': generator.CodeInstantiateOperands(), 737 'code_prologue': generator.CodePrologue(), 738 'code_epilogue': generator.CodeEpilogue(), 739 'code_parameter_list': generator.CodeParameterList(), 740 741 # Generate code to trace the execution and print C++. 742 'trace_print_outputs': generator.TracePrintOutputs(), 743 744 # Generate code to compare the results against a trace. 745 'check_instantiate_results': generator.CheckInstantiateResults(), 746 'check_instantiate_inputs': generator.CheckInstantiateInputs(), 747 'check_instantiate_references': generator.CheckInstantiateReferences(), 748 'check_results_against_references': 749 generator.CheckResultsAgainstReferences(), 750 'check_print_input': generator.CheckPrintInput(), 751 'check_print_expected': generator.CheckPrintExpected(), 752 'check_print_found': generator.CheckPrintFound(), 753 754 'test_name': generator.TestName(), 755 'isa_guard': generator.GetIsaGuard() 756 }) 757 # Create the test case and pipe it through `clang-format` before writing it. 758 with open( 759 "test/aarch32/test-{}-{}.cc".format(generator.test_type, generator.test_name), 760 "w") as f: 761 proc = subprocess.Popen([clang_format], stdin=subprocess.PIPE, 762 stdout=subprocess.PIPE) 763 out, _ = proc.communicate(generated_file.encode()) 764 f.write(out.decode()) 765 if not skip_traces: 766 # Write dummy trace files into 'test/aarch32/traces/'. 767 generator.WriteEmptyTraces("test/aarch32/traces/") 768 print("Generated {} test for \"{}\".".format(generator.test_type, generator.test_name)) 769 770 771 if __name__ == '__main__': 772 args = BuildOptions() 773 774 # Each file in `args.config_files` populates a `Generator` object. 775 generators = test_generator.parser.Parse('test/aarch32/config/data-types.json', 776 args.config_files) 777 778 # Call the `GenerateTest` function for each generator object in parallel. This 779 # will use as many processes as defined by `-jN`, which defaults to 1. 780 with multiprocessing.Pool(processes=args.jobs) as pool: 781 pool.map(functools.partial(GenerateTest, clang_format=args.clang_format, 782 skip_traces=args.skip_traces), 783 generators) 784