Files
OSACA/osaca/data/create_db_entry.py
2023-02-15 16:53:26 +01:00

181 lines
6.3 KiB
Python
Executable File

#!/usr/bin/env python3
from collections import defaultdict
from fractions import Fraction
class EntryBuilder:
@staticmethod
def compute_throughput(port_pressure):
port_occupancy = defaultdict(Fraction)
for uops, ports in port_pressure:
for p in ports:
port_occupancy[p] += Fraction(uops, len(ports))
return float(max(list(port_occupancy.values()) + [0]))
@staticmethod
def classify(operands_types):
load = "mem" in operands_types[:-1]
store = "mem" in operands_types[-1:]
vec = False
if any([vecr in operands_types for vecr in ["mm", "xmm", "ymm", "zmm"]]):
vec = True
assert not (load and store), "Can not process a combined load-store instruction."
return load, store, vec
def build_description(
self, instruction_name, operand_types, port_pressure=[], latency=0, comment=None
):
if comment:
comment = " # " + comment
else:
comment = ""
description = "- name: {}{}\n operands: {}\n".format(
instruction_name, comment, "[]" if len(operand_types) == 0 else ""
)
for ot in operand_types:
if ot == "imd":
description += " - class: immediate\n imd: int\n"
elif ot.startswith("mem"):
description += " - class: memory\n" ' base: "*"\n' ' offset: "*"\n'
if ot == "mem_simple":
description += " index: ~\n"
elif ot == "mem_complex":
description += " index: gpr\n"
else:
description += ' index: "*"\n'
description += ' scale: "*"\n'
else:
if "{k}" in ot:
description += " - class: register\n name: {}\n mask: True\n".format(
ot.replace("{k}", "")
)
else:
description += " - class: register\n name: {}\n".format(ot)
description += (
" latency: {latency}\n"
" port_pressure: {port_pressure!r}\n"
" throughput: {throughput}\n"
" uops: {uops}\n"
).format(
latency=latency,
port_pressure=port_pressure,
throughput=self.compute_throughput(port_pressure),
uops=sum([i for i, p in port_pressure]),
)
return description
def parse_port_pressure(self, port_pressure_str):
"""
Example:
1*p45+2*p0+2*p10,11 -> [[1, '45'], [2, '0'], [2, ['10', '11']]]
"""
port_pressure = []
if port_pressure_str:
for p in port_pressure_str.split("+"):
cycles, ports = p.split("*p")
ports = ports.split(",")
if len(ports) == 1:
ports = ports[0]
else:
ports = list(filter(lambda p: len(p) > 0, ports))
port_pressure.append([int(cycles), ports])
return port_pressure
def process_item(self, instruction_form, resources):
"""
Example:
('mov xmm mem', ('1*p45+2*p0', 7) -> ('mov', ['xmm', 'mem'], [[1, '45'], [2, '0']], 7)
"""
if instruction_form.startswith("[") and "]" in instruction_form:
instr_elements = instruction_form.split("]")
instr_elements = [instr_elements[0] + "]"] + instr_elements[1].strip().split(" ")
else:
instr_elements = instruction_form.split(" ")
latency = int(resources[1])
port_pressure = self.parse_port_pressure(resources[0])
instruction_name = instr_elements[0]
operand_types = instr_elements[1:]
return self.build_description(instruction_name, operand_types, port_pressure, latency)
class ArchEntryBuilder(EntryBuilder):
def build_description(self, instruction_name, operand_types, port_pressure=[], latency=0):
# Intel ICX
# LD_pressure = [[1, "23"], [1, ["2D", "3D"]]]
# LD_pressure_vec = LD_pressure
# ST_pressure = [[1, "79"], [1, "48"]]
# ST_pressure_vec = ST_pressure
# LD_lat = 5
# ST_lat = 0
# Zen3
LD_pressure = [[1, ["11", "12", "13"]]]
LD_pressure_vec = [[1, ["11", "12"]]]
ST_pressure = [[1, ["12", "13"]]]
ST_pressure_vec = [[1, ["4"]], [1, ["13"]]]
LD_lat = 4
ST_lat = 0
load, store, vec = self.classify(operand_types)
if load:
if vec:
port_pressure += LD_pressure_vec
else:
port_pressure += LD_pressure
latency += LD_lat
comment = "with load"
return EntryBuilder.build_description(
self, instruction_name, operand_types, port_pressure, latency, comment
)
if store:
if vec:
port_pressure = port_pressure + ST_pressure_vec
else:
port_pressure = port_pressure + ST_pressure
operands = ["mem" if o == "mem" else o for o in operand_types]
latency += ST_lat
return EntryBuilder.build_description(
self,
instruction_name,
operands,
port_pressure,
latency,
"with store",
)
# Register only:
return EntryBuilder.build_description(
self, instruction_name, operand_types, port_pressure, latency
)
def get_description(instruction_form, port_pressure, latency, rhs_comment=None):
entry = ArchEntryBuilder().process_item(instruction_form, (port_pressure, latency))
if rhs_comment is not None:
max_length = max([len(line) for line in entry.split("\n")])
commented_entry = ""
for line in entry.split("\n"):
commented_entry += ("{:<" + str(max_length) + "} # {}\n").format(line, rhs_comment)
entry = commented_entry
return entry
if __name__ == "__main__":
import sys
if len(sys.argv) != 4 and len(sys.argv) != 5:
print("Usage: {} <INSTRUCTION> <PORT_PRESSURE> <LATENCY> [COMMENT]".format(sys.argv[0]))
sys.exit(0)
try:
print(get_description(*sys.argv[1:]))
except KeyError:
print("Unknown architecture.")
sys.exit(1)