FEX/Scripts/UpdateInstructionCountJson.py
Ryan Houdek acc7f2fa8f FEX: Adds instruction count CI
Implements CI for tracking instruction counts for generate blocks of
code when transforming from x86 to ARM64 assembly.

This will end up encompassing every instruction in our instruction
tables similarly to how our assembly tests try to test everything in our
instruction tables.

Incidentally, the data for this CI is generated using our assembly
tests. By enabling disassembly and instruction stats when executing a
suite of instructions, this gives the stats that can be added to a json
file.

The current implementation only implements the SecondGroup table of
instructions because it is a relatively small table and has known
inefficiencies in the instruction implementations. As this gets merged I
will be adding more tables of instructions to additional json files for
testing.

These JSON files will support adjusting CPU features regardless of the
host features so it can test implementations depending on different CPU
features. This will let us test things like one instruction having
different "optimal" implementations depending on if it supports SVE128,
SVE256, SVEI8MM, etc.

This initial instruction auditing is what found the bug in our vector
shift instructions by size of zero. If inspecting the result of the CI
run, you can tell that these instructions still aren't "optimal" because
they are doing loads and stores that can be eliminated.

The "Optimal" in the JSON is purely for human readable and grepping
ability to see what is optimal versus not. Same with the "Comment"
section.

According to my auditing spreadsheet, the total number of instructions
that will end up in these json files will be about 1000, but we will
likely end up with more since there will be edge cases that can be more
optimal depending on arguments.
2023-08-11 09:10:36 -07:00

66 lines
2.1 KiB
Python
Executable File

#!/usr/bin/python3
import json
import logging
import sys
logger = logging.getLogger()
logger.setLevel(logging.ERROR)
def update_performance_numbers(performance_json_path, performance_json, new_json_numbers):
for key, items in new_json_numbers.items():
if len(key) == 0:
continue
if not key in performance_json["Instructions"]:
logging.error("{} didn't exist in performance json file?".format(key))
return 1
performance_json["Instructions"][key]["ExpectedInstructionCount"] = items
# Output to the original file.
with open(performance_json_path, "w") as json_file:
json.dump(performance_json, json_file, indent=2)
def main():
if sys.version_info[0] < 3:
logging.critical ("Python 3 or a more recent version is required.")
if (len(sys.argv) < 3):
logging.critical ("usage: %s <PerformanceTests.json> <NewNumbers.json>" % (sys.argv[0]))
performance_json_path = sys.argv[1]
new_json_numbers = sys.argv[2]
try:
with open(new_json_numbers) as json_file:
new_json_numbers_text = json_file.read()
except IOError:
# If there isn't any new json numbers for this file, then it is safe to skip.
return 0
try:
with open(performance_json_path) as json_file:
performance_json_text = json_file.read()
except IOError:
logging.error("IOError!")
return 1
try:
performance_json_data = json.loads(performance_json_text)
if not isinstance(performance_json_data, dict):
raise TypeError('JSON data must be a dict')
new_json_numbers_data = json.loads(new_json_numbers_text)
if not isinstance(new_json_numbers_data, dict):
raise TypeError('JSON data must be a dict')
return update_performance_numbers(performance_json_path, performance_json_data, new_json_numbers_data)
except ValueError as ve:
logging.error(f'JSON error: {ve}')
return 1
return 0
if __name__ == "__main__":
# execute only if run as a script
sys.exit(main())