#!/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: {} [COMMENT]".format(sys.argv[0])) sys.exit(0) try: print(get_description(*sys.argv[1:])) except KeyError: print("Unknown architecture.") sys.exit(1)