mirror of
https://github.com/RRZE-HPC/OSACA.git
synced 2025-12-15 16:40:05 +01:00
Changes to accomodate the new OO style
This commit is contained in:
0
osaca/db_interface.py
Executable file → Normal file
0
osaca/db_interface.py
Executable file → Normal file
0
osaca/frontend.py
Executable file → Normal file
0
osaca/frontend.py
Executable file → Normal file
5
osaca/osaca.py
Executable file → Normal file
5
osaca/osaca.py
Executable file → Normal file
@@ -430,10 +430,7 @@ def get_unmatched_instruction_ratio(kernel):
|
||||
"""Return ratio of unmatched from total instructions in kernel."""
|
||||
unmatched_counter = 0
|
||||
for instruction in kernel:
|
||||
if (
|
||||
INSTR_FLAGS.TP_UNKWN in instruction["flags"]
|
||||
and INSTR_FLAGS.LT_UNKWN in instruction["flags"]
|
||||
):
|
||||
if INSTR_FLAGS.TP_UNKWN in instruction.flags and INSTR_FLAGS.LT_UNKWN in instruction.flags:
|
||||
unmatched_counter += 1
|
||||
return unmatched_counter / len(kernel)
|
||||
|
||||
|
||||
@@ -4,18 +4,6 @@ from osaca.parser.directive import DirectiveOperand
|
||||
|
||||
|
||||
class InstructionForm:
|
||||
# Identifiers for operand types
|
||||
COMMENT_ID = "comment"
|
||||
DIRECTIVE_ID = "directive"
|
||||
IMMEDIATE_ID = "immediate"
|
||||
LABEL_ID = "label"
|
||||
IDENTIFIER_ID = "identifier"
|
||||
MEMORY_ID = "memory"
|
||||
REGISTER_ID = "register"
|
||||
SEGMENT_EXT_ID = "segment_extension"
|
||||
INSTRUCTION_ID = "instruction"
|
||||
OPERANDS_ID = "operands"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
INSTRUCTION_ID=None,
|
||||
@@ -91,6 +79,18 @@ class InstructionForm:
|
||||
def flags(self):
|
||||
return self._FLAGS
|
||||
|
||||
@property
|
||||
def throughput(self):
|
||||
return self._THROUGHPUT
|
||||
|
||||
@property
|
||||
def latency(self):
|
||||
return self._LATENCY
|
||||
|
||||
@property
|
||||
def latency_wo_load(self):
|
||||
return self._LATENCY_WO_LOAD
|
||||
|
||||
@semantic_operands.setter
|
||||
def semantic_operands(self, semantic_operands):
|
||||
self._SEMANTIC_OPERANDS = semantic_operands
|
||||
@@ -135,6 +135,18 @@ class InstructionForm:
|
||||
def flags(self, flags):
|
||||
self._FLAGS = flags
|
||||
|
||||
@throughput.setter
|
||||
def throughput(self, throughput):
|
||||
self._THROUGHPUT = throughput
|
||||
|
||||
@latency.setter
|
||||
def latency(self, latency):
|
||||
self._LATENCY = latency
|
||||
|
||||
@latency_wo_load.setter
|
||||
def latency_wo_load(self, latency_wo_load):
|
||||
self._LATENCY_WO_LOAD = latency_wo_load
|
||||
|
||||
def __repr__(self):
|
||||
return f"InstructionForm(INSTRUCTION_ID={self._INSTRUCTION_ID}, OPERANDS_ID={self._OPERANDS_ID}, DIRECTIVE_ID={self._DIRECTIVE_ID}, COMMENT_ID={self._COMMENT_ID}, LABEL_ID={self._LABEL_ID}, LINE={self._LINE}, LINE_NUMBER={self._LINE_NUMBER}, SEMANTIC_OPERANDS={self._SEMANTIC_OPERANDS})"
|
||||
|
||||
|
||||
@@ -386,8 +386,8 @@ class ParserX86ATT(BaseParser):
|
||||
def is_reg_dependend_of(self, reg_a, reg_b):
|
||||
"""Check if ``reg_a`` is dependent on ``reg_b``"""
|
||||
# Normalize name
|
||||
reg_a_name = reg_a["name"].upper()
|
||||
reg_b_name = reg_b["name"].upper()
|
||||
reg_a_name = reg_a.name.upper()
|
||||
reg_b_name = reg_b.name.upper()
|
||||
|
||||
# Check if they are the same registers
|
||||
if reg_a_name == reg_b_name:
|
||||
@@ -428,8 +428,8 @@ class ParserX86ATT(BaseParser):
|
||||
|
||||
def is_basic_gpr(self, register):
|
||||
"""Check if register is a basic general purpose register (ebi, rax, ...)"""
|
||||
if any(char.isdigit() for char in register["name"]) or any(
|
||||
register["name"].lower().startswith(x) for x in ["mm", "xmm", "ymm", "zmm"]
|
||||
if any(char.isdigit() for char in register.name) or any(
|
||||
register.name.lower().startswith(x) for x in ["mm", "xmm", "ymm", "zmm"]
|
||||
):
|
||||
return False
|
||||
return True
|
||||
@@ -446,7 +446,7 @@ class ParserX86ATT(BaseParser):
|
||||
"""Check if register is a vector register"""
|
||||
if register is None:
|
||||
return False
|
||||
if register["name"].rstrip(string.digits).lower() in [
|
||||
if register.name.rstrip(string.digits).lower() in [
|
||||
"mm",
|
||||
"xmm",
|
||||
"ymm",
|
||||
|
||||
@@ -69,9 +69,7 @@ class ArchSemantics(ISASemantics):
|
||||
indices = [port_list.index(p) for p in ports]
|
||||
# check if port sum of used ports for uop are unbalanced
|
||||
port_sums = self._to_list(itemgetter(*indices)(self.get_throughput_sum(kernel)))
|
||||
instr_ports = self._to_list(
|
||||
itemgetter(*indices)(instruction_form.port_pressure)
|
||||
)
|
||||
instr_ports = self._to_list(itemgetter(*indices)(instruction_form.port_pressure))
|
||||
if len(set(port_sums)) > 1:
|
||||
# balance ports
|
||||
# init list for keeping track of the current change
|
||||
@@ -110,9 +108,7 @@ class ArchSemantics(ISASemantics):
|
||||
][0]
|
||||
instruction_form.port_pressure[zero_index] = 0.0
|
||||
# Remove from further balancing
|
||||
indices = [
|
||||
p for p in indices if instruction_form.port_pressure[p] > 0
|
||||
]
|
||||
indices = [p for p in indices if instruction_form.port_pressure[p] > 0]
|
||||
instr_ports = self._to_list(
|
||||
itemgetter(*indices)(instruction_form.port_pressure)
|
||||
)
|
||||
@@ -193,6 +189,7 @@ class ArchSemantics(ISASemantics):
|
||||
instruction_data = self._machine_model.get_instruction(
|
||||
instruction_form.instruction, instruction_form.operands
|
||||
)
|
||||
|
||||
if (
|
||||
not instruction_data
|
||||
and self._isa == "x86"
|
||||
|
||||
@@ -14,6 +14,11 @@ import ruamel.yaml
|
||||
from osaca import __version__, utils
|
||||
from osaca.parser import ParserX86ATT
|
||||
from ruamel.yaml.compat import StringIO
|
||||
from osaca.parser.operand import Operand
|
||||
from osaca.parser.memory import MemoryOperand
|
||||
from osaca.parser.register import RegisterOperand
|
||||
from osaca.parser.immediate import ImmediateOperand
|
||||
from osaca.parser.identifier import IdentifierOperand
|
||||
|
||||
|
||||
class MachineModel(object):
|
||||
@@ -21,7 +26,7 @@ class MachineModel(object):
|
||||
INTERNAL_VERSION = 1 # increase whenever self._data format changes to invalidate cache!
|
||||
_runtime_cache = {}
|
||||
|
||||
def __init__(self, arch=None, path_to_yaml=None, isa=None, lazy=False):
|
||||
def __init__(self, arch=None, path_to_yaml=None, isa=None, lazy=True):
|
||||
if not arch and not path_to_yaml:
|
||||
if not isa:
|
||||
raise ValueError("One of arch, path_to_yaml and isa must be specified")
|
||||
@@ -97,6 +102,7 @@ class MachineModel(object):
|
||||
# Normalize instruction_form names (to UPPERCASE) and build dict for faster access:
|
||||
self._data["instruction_forms_dict"] = defaultdict(list)
|
||||
for iform in self._data["instruction_forms"]:
|
||||
print(iform)
|
||||
iform["name"] = iform["name"].upper()
|
||||
self._data["instruction_forms_dict"][iform["name"]].append(iform)
|
||||
self._data["internal_version"] = self.INTERNAL_VERSION
|
||||
@@ -128,6 +134,7 @@ class MachineModel(object):
|
||||
if name is None:
|
||||
return None
|
||||
name_matched_iforms = self._data["instruction_forms_dict"].get(name.upper(), [])
|
||||
# print(name_matched_iforms)
|
||||
try:
|
||||
return next(
|
||||
instruction_form
|
||||
@@ -264,7 +271,7 @@ class MachineModel(object):
|
||||
def get_full_instruction_name(instruction_form):
|
||||
"""Get one instruction name string including the mnemonic and all operands."""
|
||||
operands = []
|
||||
for op in instruction_form["operands"]:
|
||||
for op in instruction_form.operands:
|
||||
op_attrs = [
|
||||
y + ":" + str(op[y])
|
||||
for y in list(filter(lambda x: True if x != "class" else False, op))
|
||||
@@ -526,7 +533,9 @@ class MachineModel(object):
|
||||
def _check_operands(self, i_operand, operand):
|
||||
"""Check if the types of operand ``i_operand`` and ``operand`` match."""
|
||||
# check for wildcard
|
||||
if self.WILDCARD in operand.name:
|
||||
if (isinstance(operand, Operand) and operand.name == self.WILDCARD) or (
|
||||
not isinstance(operand, Operand) and self.WILDCARD in operand
|
||||
):
|
||||
if (
|
||||
"class" in i_operand
|
||||
and i_operand["class"] == "register"
|
||||
@@ -601,25 +610,27 @@ class MachineModel(object):
|
||||
|
||||
def _check_x86_operands(self, i_operand, operand):
|
||||
"""Check if the types of operand ``i_operand`` and ``operand`` match."""
|
||||
if "class" in operand.name:
|
||||
# compare two DB entries
|
||||
return self._compare_db_entries(i_operand, operand)
|
||||
# if "class" in operand.name:
|
||||
# compare two DB entries
|
||||
# return self._compare_db_entries(i_operand, operand)
|
||||
# register
|
||||
if "register" in operand.name:
|
||||
if isinstance(operand, RegisterOperand):
|
||||
if i_operand["class"] != "register":
|
||||
return False
|
||||
return self._is_x86_reg_type(i_operand, operand, consider_masking=False)
|
||||
# memory
|
||||
if "memory" in operand.name:
|
||||
if isinstance(operand, MemoryOperand):
|
||||
if i_operand["class"] != "memory":
|
||||
return False
|
||||
return self._is_x86_mem_type(i_operand, operand)
|
||||
# immediate
|
||||
if "immediate" in operand.name or operand.value != None:
|
||||
if isinstance(operand, ImmediateOperand):
|
||||
# if "immediate" in operand.name or operand.value != None:
|
||||
return i_operand["class"] == "immediate" and i_operand["imd"] == "int"
|
||||
# identifier (e.g., labels)
|
||||
if "identifier" in operand.name:
|
||||
if isinstance(operand, IdentifierOperand):
|
||||
return i_operand["class"] == "identifier"
|
||||
return self._compare_db_entries(i_operand, operand)
|
||||
|
||||
def _compare_db_entries(self, operand_1, operand_2):
|
||||
"""Check if operand types in DB format (i.e., not parsed) match."""
|
||||
@@ -676,29 +687,29 @@ class MachineModel(object):
|
||||
return True
|
||||
return False
|
||||
# check for wildcards
|
||||
if i_reg_name == self.WILDCARD or reg["name"] == self.WILDCARD:
|
||||
if i_reg_name == self.WILDCARD or reg.name == self.WILDCARD:
|
||||
return True
|
||||
# differentiate between vector registers (mm, xmm, ymm, zmm) and others (gpr)
|
||||
parser_x86 = ParserX86ATT()
|
||||
if parser_x86.is_vector_register(reg):
|
||||
if reg["name"].rstrip(string.digits).lower() == i_reg_name:
|
||||
if reg.name.rstrip(string.digits).lower() == i_reg_name:
|
||||
# Consider masking and zeroing for AVX512
|
||||
if consider_masking:
|
||||
mask_ok = zero_ok = True
|
||||
if "mask" in reg or "mask" in i_reg:
|
||||
if reg.mask != None or "mask" in i_reg:
|
||||
# one instruction is missing the masking while the other has it
|
||||
mask_ok = False
|
||||
# check for wildcard
|
||||
if (
|
||||
(
|
||||
"mask" in reg
|
||||
and reg["mask"].rstrip(string.digits).lower() == i_reg.get("mask")
|
||||
reg.mask != None
|
||||
and reg.mask.rstrip(string.digits).lower() == i_reg.get("mask")
|
||||
)
|
||||
or reg.get("mask") == self.WILDCARD
|
||||
or reg.mask == self.WILDCARD
|
||||
or i_reg.get("mask") == self.WILDCARD
|
||||
):
|
||||
mask_ok = True
|
||||
if bool("zeroing" in reg) ^ bool("zeroing" in i_reg):
|
||||
if bool(reg.zeroing) ^ bool("zeroing" in i_reg):
|
||||
# one instruction is missing zeroing while the other has it
|
||||
zero_ok = False
|
||||
# check for wildcard
|
||||
@@ -776,48 +787,48 @@ class MachineModel(object):
|
||||
if (
|
||||
# check base
|
||||
(
|
||||
(mem["base"] is None and i_mem["base"] is None)
|
||||
(mem.base is None and i_mem["base"] is None)
|
||||
or i_mem["base"] == self.WILDCARD
|
||||
or self._is_x86_reg_type(i_mem["base"], mem["base"])
|
||||
or self._is_x86_reg_type(i_mem["base"], mem.base)
|
||||
)
|
||||
# check offset
|
||||
and (
|
||||
mem["offset"] == i_mem["offset"]
|
||||
mem.offset == i_mem["offset"]
|
||||
or i_mem["offset"] == self.WILDCARD
|
||||
or (
|
||||
mem["offset"] is not None
|
||||
and "identifier" in mem["offset"]
|
||||
mem.offset is not None
|
||||
and "identifier" in mem.offset
|
||||
and i_mem["offset"] == "identifier"
|
||||
)
|
||||
or (
|
||||
mem["offset"] is not None
|
||||
and "value" in mem["offset"]
|
||||
mem.offset is not None
|
||||
and "value" in mem.offset
|
||||
and (
|
||||
i_mem["offset"] == "imd"
|
||||
or (i_mem["offset"] is None and mem["offset"]["value"] == "0")
|
||||
i_mem.offset == "imd"
|
||||
or (i_mem["offset"] is None and mem.offset["value"] == "0")
|
||||
)
|
||||
)
|
||||
or (
|
||||
mem["offset"] is not None
|
||||
and "identifier" in mem["offset"]
|
||||
mem.offset is not None
|
||||
and "identifier" in mem.offset
|
||||
and i_mem["offset"] == "id"
|
||||
)
|
||||
)
|
||||
# check index
|
||||
and (
|
||||
mem["index"] == i_mem["index"]
|
||||
mem.index == i_mem["index"]
|
||||
or i_mem["index"] == self.WILDCARD
|
||||
or (
|
||||
mem["index"] is not None
|
||||
and "name" in mem["index"]
|
||||
and self._is_x86_reg_type(i_mem["index"], mem["index"])
|
||||
mem.index is not None
|
||||
and "name" in mem.index
|
||||
and self._is_x86_reg_type(i_mem["index"], mem.index)
|
||||
)
|
||||
)
|
||||
# check scale
|
||||
and (
|
||||
mem["scale"] == i_mem["scale"]
|
||||
mem.scale == i_mem["scale"]
|
||||
or i_mem["scale"] == self.WILDCARD
|
||||
or (mem["scale"] != 1 and i_mem["scale"] != 1)
|
||||
or (mem.scale != 1 and i_mem["scale"] != 1)
|
||||
)
|
||||
):
|
||||
return True
|
||||
|
||||
@@ -4,39 +4,10 @@ from itertools import chain
|
||||
from osaca import utils
|
||||
from osaca.parser import AttrDict, ParserAArch64, ParserX86ATT
|
||||
from osaca.parser.memory import MemoryOperand
|
||||
from osaca.parser.register import RegisterOperand
|
||||
|
||||
from .hw_model import MachineModel
|
||||
|
||||
class SemanticForm:
|
||||
def __init__(self, SOURCE_ID = [], DESTINATION_ID = [], SRC_DST = []):
|
||||
self._SOURCE_ID = SOURCE_ID
|
||||
self._DESTINATION_ID = DESTINATION_ID
|
||||
self._SRC_DST = SRC_DST
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
return self._SOURCE_ID
|
||||
|
||||
@source.setter
|
||||
def source(self, source):
|
||||
self._SOURCE_ID = source
|
||||
|
||||
@property
|
||||
def destination(self):
|
||||
return self._DESTINATION_ID
|
||||
|
||||
@destination.setter
|
||||
def destination(self, destination):
|
||||
self._DESTINATION_ID = destination
|
||||
|
||||
@property
|
||||
def src_dst(self):
|
||||
return self._SRC_DST
|
||||
|
||||
@src_dst.setter
|
||||
def src_dst(self, src_dst):
|
||||
self._SRC_DST = src_dst
|
||||
|
||||
|
||||
class INSTR_FLAGS:
|
||||
"""
|
||||
@@ -77,9 +48,7 @@ class ISASemantics(object):
|
||||
"""Update instruction form dictionary with source, destination and flag information."""
|
||||
# if the instruction form doesn't have operands or is None, there's nothing to do
|
||||
if instruction_form.operands is None or instruction_form.instruction is None:
|
||||
instruction_form.semantic_operands = SemanticForm(
|
||||
SOURCE_ID = [], DESTINATION_ID = [], SRC_DST = []
|
||||
)
|
||||
instruction_form.semantic_operands = {"source": [], "destination": [], "src_dst": []}
|
||||
return
|
||||
# check if instruction form is in ISA yaml, otherwise apply standard operand assignment
|
||||
# (one dest, others source)
|
||||
@@ -111,8 +80,7 @@ class ISASemantics(object):
|
||||
# Couldn't found instruction form in ISA DB
|
||||
assign_default = True
|
||||
# check for equivalent register-operands DB entry if LD/ST
|
||||
|
||||
if isinstance(operands, MemoryOperand) and operands.name == "memory":
|
||||
if any([isinstance(op, MemoryOperand) for op in operands]):
|
||||
operands_reg = self.substitute_mem_address(instruction_form.operands)
|
||||
isa_data_reg = self._isa_model.get_instruction(
|
||||
instruction_form.instruction, operands_reg
|
||||
@@ -146,46 +114,34 @@ class ISASemantics(object):
|
||||
op_dict["src_dst"] = []
|
||||
# post-process pre- and post-indexing for aarch64 memory operands
|
||||
if self._isa == "aarch64":
|
||||
for operand in [op for op in op_dict["source"] if "memory" in op]:
|
||||
post_indexed = (
|
||||
"post_indexed" in operand["memory"] and operand["memory"]["post_indexed"]
|
||||
)
|
||||
pre_indexed = (
|
||||
"pre_indexed" in operand["memory"] and operand["memory"]["pre_indexed"]
|
||||
)
|
||||
for operand in [op for op in op_dict["source"] if isinstance(op, MemoryOperand)]:
|
||||
post_indexed = operand.post_indexed
|
||||
pre_indexed = operand.pre_indexed
|
||||
if post_indexed or pre_indexed:
|
||||
op_dict["src_dst"].append(
|
||||
AttrDict.convert_dict(
|
||||
{
|
||||
"register": operand["memory"]["base"],
|
||||
"pre_indexed": pre_indexed,
|
||||
"post_indexed": post_indexed,
|
||||
}
|
||||
)
|
||||
{
|
||||
"register": operand.base,
|
||||
"pre_indexed": pre_indexed,
|
||||
"post_indexed": post_indexed,
|
||||
}
|
||||
)
|
||||
for operand in [op for op in op_dict["destination"] if "memory" in op]:
|
||||
post_indexed = (
|
||||
"post_indexed" in operand["memory"] and operand["memory"]["post_indexed"]
|
||||
)
|
||||
pre_indexed = (
|
||||
"pre_indexed" in operand["memory"] and operand["memory"]["pre_indexed"]
|
||||
)
|
||||
for operand in [op for op in op_dict["destination"] if isinstance(op, MemoryOperand)]:
|
||||
post_indexed = operand.post_indexed
|
||||
pre_indexed = operand.pre_indexed
|
||||
if post_indexed or pre_indexed:
|
||||
op_dict["src_dst"].append(
|
||||
AttrDict.convert_dict(
|
||||
{
|
||||
"register": operand["memory"]["base"],
|
||||
"pre_indexed": pre_indexed,
|
||||
"post_indexed": post_indexed,
|
||||
}
|
||||
)
|
||||
{
|
||||
"register": operand.base,
|
||||
"pre_indexed": pre_indexed,
|
||||
"post_indexed": post_indexed,
|
||||
}
|
||||
)
|
||||
# store operand list in dict and reassign operand key/value pair
|
||||
instruction_form.semantic_operands = AttrDict.convert_dict(op_dict)
|
||||
instruction_form.semantic_operands = op_dict
|
||||
# assign LD/ST flags
|
||||
instruction_form.flags = (
|
||||
instruction_form.flags if instruction_form.flags != [] else []
|
||||
)
|
||||
# instruction_form.flags = (
|
||||
# instruction_form.flags if "flags" in instruction_form else []
|
||||
# )
|
||||
if self._has_load(instruction_form):
|
||||
instruction_form.flags += [INSTR_FLAGS.HAS_LD]
|
||||
if self._has_store(instruction_form):
|
||||
@@ -198,15 +154,15 @@ class ISASemantics(object):
|
||||
Empty dict if no changes of registers occured. None for registers with unknown changes.
|
||||
If only_postindexed is True, only considers changes due to post_indexed memory references.
|
||||
"""
|
||||
if instruction_form.get("instruction") is None:
|
||||
if instruction_form.instruction is None:
|
||||
return {}
|
||||
dest_reg_names = [
|
||||
op.register.get("prefix", "") + op.register.name
|
||||
op.prefix if op.prefix != None else "" + op.name
|
||||
for op in chain(
|
||||
instruction_form.semantic_operands.destination,
|
||||
instruction_form.semantic_operands.src_dst,
|
||||
instruction_form.semantic_operands["destination"],
|
||||
instruction_form.semantic_operands["src_dst"],
|
||||
)
|
||||
if "register" in op
|
||||
if isinstance(op, RegisterOperand)
|
||||
]
|
||||
isa_data = self._isa_model.get_instruction(
|
||||
instruction_form.instruction, instruction_form.operands
|
||||
@@ -243,7 +199,7 @@ class ISASemantics(object):
|
||||
operand_state = {} # e.g., {'op1': {'name': 'rax', 'value': 0}} 0 means unchanged
|
||||
|
||||
for o in instruction_form.operands:
|
||||
if "pre_indexed" in o.get("memory", {}):
|
||||
if isinstance(o, MemoryOperand) and o.pre_indexed:
|
||||
# Assuming no isa_data.operation
|
||||
if isa_data is not None and isa_data.get("operation", None) is not None:
|
||||
raise ValueError(
|
||||
@@ -340,20 +296,20 @@ class ISASemantics(object):
|
||||
def _has_load(self, instruction_form):
|
||||
"""Check if instruction form performs a LOAD"""
|
||||
for operand in chain(
|
||||
instruction_form.semantic_operands.source,
|
||||
instruction_form.semantic_operands.src_dst,
|
||||
instruction_form.semantic_operands["source"],
|
||||
instruction_form.semantic_operands["src_dst"],
|
||||
):
|
||||
if operand.name == "memory":
|
||||
if isinstance(operand, MemoryOperand):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _has_store(self, instruction_form):
|
||||
"""Check if instruction form perfroms a STORE"""
|
||||
for operand in chain(
|
||||
instruction_form.semantic_operands.destination,
|
||||
instruction_form.semantic_operands.src_dst,
|
||||
instruction_form.semantic_operands["destination"],
|
||||
instruction_form.semantic_operands["src_dst"],
|
||||
):
|
||||
if "memory" in operand:
|
||||
if isinstance(operand, MemoryOperand):
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -386,7 +342,9 @@ class ISASemantics(object):
|
||||
|
||||
def substitute_mem_address(self, operands):
|
||||
"""Create memory wildcard for all memory operands"""
|
||||
return [self._create_reg_wildcard() if "memory" in op else op for op in operands]
|
||||
return [
|
||||
self._create_reg_wildcard() if isinstance(op, MemoryOperand) else op for op in operands
|
||||
]
|
||||
|
||||
def _create_reg_wildcard(self):
|
||||
"""Wildcard constructor"""
|
||||
|
||||
@@ -9,6 +9,8 @@ from multiprocessing import Manager, Process, cpu_count
|
||||
|
||||
import networkx as nx
|
||||
from osaca.semantics import INSTR_FLAGS, ArchSemantics, MachineModel
|
||||
from osaca.parser.memory import MemoryOperand
|
||||
from osaca.parser.register import RegisterOperand
|
||||
|
||||
|
||||
class KernelDG(nx.DiGraph):
|
||||
@@ -60,8 +62,7 @@ class KernelDG(nx.DiGraph):
|
||||
dg = nx.DiGraph()
|
||||
for i, instruction_form in enumerate(kernel):
|
||||
dg.add_node(instruction_form.line_number)
|
||||
print(dg.nodes[instruction_form.line_number])
|
||||
dg.nodes[instruction_form.line_number].instruction_form = instruction_form
|
||||
dg.nodes[instruction_form.line_number]["instruction_form"] = instruction_form
|
||||
# add load as separate node if existent
|
||||
if (
|
||||
INSTR_FLAGS.HAS_LD in instruction_form.flags
|
||||
@@ -271,8 +272,8 @@ class KernelDG(nx.DiGraph):
|
||||
if instruction_form.semantic_operands is None:
|
||||
return
|
||||
for dst in chain(
|
||||
instruction_form.semantic_operands.destination,
|
||||
instruction_form.semantic_operands.src_dst,
|
||||
instruction_form.semantic_operands["destination"],
|
||||
instruction_form.semantic_operands["src_dst"],
|
||||
):
|
||||
# TODO instructions before must be considered as well, if they update registers
|
||||
# not used by insruction_form. E.g., validation/build/A64FX/gcc/O1/gs-2d-5pt.marked.s
|
||||
@@ -281,27 +282,32 @@ class KernelDG(nx.DiGraph):
|
||||
for i, instr_form in enumerate(instructions):
|
||||
self._update_reg_changes(instr_form, register_changes)
|
||||
# print(" TO", instr_form.line, register_changes)
|
||||
if "register" in dst:
|
||||
if isinstance(dst, RegisterOperand):
|
||||
# read of register
|
||||
if self.is_read(dst.register, instr_form):
|
||||
if self.is_read(dst, instr_form):
|
||||
if dst.get("pre_indexed", False) or dst.get("post_indexed", False):
|
||||
yield instr_form, ["p_indexed"]
|
||||
else:
|
||||
yield instr_form, []
|
||||
# write to register -> abort
|
||||
if self.is_written(dst.register, instr_form):
|
||||
if self.is_written(dst, instr_form):
|
||||
break
|
||||
if "flag" in dst and flag_dependencies:
|
||||
if (
|
||||
not isinstance(dst, RegisterOperand)
|
||||
and not isinstance(dst, MemoryOperandOperand)
|
||||
and "flag" in dst
|
||||
and flag_dependencies
|
||||
):
|
||||
# read of flag
|
||||
if self.is_read(dst.flag, instr_form):
|
||||
yield instr_form, []
|
||||
# write to flag -> abort
|
||||
if self.is_written(dst.flag, instr_form):
|
||||
break
|
||||
if "memory" in dst:
|
||||
if isinstance(dst, MemoryOperand):
|
||||
# base register is altered during memory access
|
||||
if "pre_indexed" in dst.memory:
|
||||
if self.is_written(dst.memory.base, instr_form):
|
||||
if dist.pre_indexed != None:
|
||||
if self.is_written(dst.base, instr_form):
|
||||
break
|
||||
# if dst.memory.base:
|
||||
# if self.is_read(dst.memory.base, instr_form):
|
||||
@@ -309,18 +315,18 @@ class KernelDG(nx.DiGraph):
|
||||
# if dst.memory.index:
|
||||
# if self.is_read(dst.memory.index, instr_form):
|
||||
# yield instr_form, []
|
||||
if "post_indexed" in dst.memory:
|
||||
if dst.post_indexed:
|
||||
# Check for read of base register until overwrite
|
||||
if self.is_written(dst.memory.base, instr_form):
|
||||
if self.is_written(dst.base, instr_form):
|
||||
break
|
||||
# TODO record register changes
|
||||
# (e.g., mov, leaadd, sub, inc, dec) in instructions[:i]
|
||||
# and pass to is_memload and is_memstore to consider relevance.
|
||||
# load from same location (presumed)
|
||||
if self.is_memload(dst.memory, instr_form, register_changes):
|
||||
if self.is_memload(dst, instr_form, register_changes):
|
||||
yield instr_form, ["storeload_dep"]
|
||||
# store to same location (presumed)
|
||||
if self.is_memstore(dst.memory, instr_form, register_changes):
|
||||
if self.is_memstore(dst, instr_form, register_changes):
|
||||
break
|
||||
self._update_reg_changes(instr_form, register_changes, only_postindexed=True)
|
||||
|
||||
@@ -364,32 +370,32 @@ class KernelDG(nx.DiGraph):
|
||||
if instruction_form.semantic_operands is None:
|
||||
return is_read
|
||||
for src in chain(
|
||||
instruction_form.semantic_operands.source,
|
||||
instruction_form.semantic_operands.src_dst,
|
||||
instruction_form.semantic_operands["source"],
|
||||
instruction_form.semantic_operands["src_dst"],
|
||||
):
|
||||
if "register" in src:
|
||||
is_read = self.parser.is_reg_dependend_of(register, src.register) or is_read
|
||||
if "flag" in src:
|
||||
if isinstance(src, RegisterOperand):
|
||||
is_read = self.parser.is_reg_dependend_of(register, src) or is_read
|
||||
if (
|
||||
not isinstance(src, RegisterOperand)
|
||||
and not isinstance(src, MemoryOperand)
|
||||
and "flag" in src
|
||||
):
|
||||
is_read = self.parser.is_flag_dependend_of(register, src.flag) or is_read
|
||||
if "memory" in src:
|
||||
if src.memory.base is not None:
|
||||
is_read = self.parser.is_reg_dependend_of(register, src.memory.base) or is_read
|
||||
if src.memory.index is not None:
|
||||
is_read = (
|
||||
self.parser.is_reg_dependend_of(register, src.memory.index) or is_read
|
||||
)
|
||||
if isinstance(src, MemoryOperand):
|
||||
if src.base is not None:
|
||||
is_read = self.parser.is_reg_dependend_of(register, src.base) or is_read
|
||||
if src.index is not None:
|
||||
is_read = self.parser.is_reg_dependend_of(register, src.index) or is_read
|
||||
# Check also if read in destination memory address
|
||||
for dst in chain(
|
||||
instruction_form.semantic_operands.destination,
|
||||
instruction_form.semantic_operands.src_dst,
|
||||
instruction_form.semantic_operands["destination"],
|
||||
instruction_form.semantic_operands["src_dst"],
|
||||
):
|
||||
if "memory" in dst:
|
||||
if dst.memory.base is not None:
|
||||
is_read = self.parser.is_reg_dependend_of(register, dst.memory.base) or is_read
|
||||
if dst.memory.index is not None:
|
||||
is_read = (
|
||||
self.parser.is_reg_dependend_of(register, dst.memory.index) or is_read
|
||||
)
|
||||
if isinstance(dst, MemoryOperand):
|
||||
if dst.base is not None:
|
||||
is_read = self.parser.is_reg_dependend_of(register, dst.base) or is_read
|
||||
if dst.index is not None:
|
||||
is_read = self.parser.is_reg_dependend_of(register, dst.index) or is_read
|
||||
return is_read
|
||||
|
||||
def is_memload(self, mem, instruction_form, register_changes={}):
|
||||
@@ -455,28 +461,28 @@ class KernelDG(nx.DiGraph):
|
||||
if instruction_form.semantic_operands is None:
|
||||
return is_written
|
||||
for dst in chain(
|
||||
instruction_form.semantic_operands.destination,
|
||||
instruction_form.semantic_operands.src_dst,
|
||||
instruction_form.semantic_operands["destination"],
|
||||
instruction_form.semantic_operands["src_dst"],
|
||||
):
|
||||
if "register" in dst:
|
||||
is_written = self.parser.is_reg_dependend_of(register, dst.register) or is_written
|
||||
if "flag" in dst:
|
||||
if isinstance(dst, RegisterOperand):
|
||||
is_written = self.parser.is_reg_dependend_of(register, dst) or is_written
|
||||
if (
|
||||
not isinstance(dst, RegisterOperand)
|
||||
and not isinstance(dst, MemoryOperand)
|
||||
and "flag" in dst
|
||||
):
|
||||
is_written = self.parser.is_flag_dependend_of(register, dst.flag) or is_written
|
||||
if "memory" in dst:
|
||||
if "pre_indexed" in dst.memory or "post_indexed" in dst.memory:
|
||||
is_written = (
|
||||
self.parser.is_reg_dependend_of(register, dst.memory.base) or is_written
|
||||
)
|
||||
if isinstance(dst, MemoryOperand):
|
||||
if dst.pre_indexed or dst.post_indexed:
|
||||
is_written = self.parser.is_reg_dependend_of(register, dst.base) or is_written
|
||||
# Check also for possible pre- or post-indexing in memory addresses
|
||||
for src in chain(
|
||||
instruction_form.semantic_operands.source,
|
||||
instruction_form.semantic_operands.src_dst,
|
||||
instruction_form.semantic_operands["source"],
|
||||
instruction_form.semantic_operands["src_dst"],
|
||||
):
|
||||
if "memory" in src:
|
||||
if "pre_indexed" in src.memory or "post_indexed" in src.memory:
|
||||
is_written = (
|
||||
self.parser.is_reg_dependend_of(register, src.memory.base) or is_written
|
||||
)
|
||||
if isinstance(src, MemoryOperand):
|
||||
if src.pre_indexed or src.post_indexed:
|
||||
is_written = self.parser.is_reg_dependend_of(register, src.base) or is_written
|
||||
return is_written
|
||||
|
||||
def is_memstore(self, mem, instruction_form, register_changes={}):
|
||||
@@ -485,10 +491,10 @@ class KernelDG(nx.DiGraph):
|
||||
if instruction_form.semantic_operands is None:
|
||||
return is_store
|
||||
for dst in chain(
|
||||
instruction_form.semantic_operands.destination,
|
||||
instruction_form.semantic_operands.src_dst,
|
||||
instruction_form.semantic_operands["destination"],
|
||||
instruction_form.semantic_operands["src_dst"],
|
||||
):
|
||||
if "memory" in dst:
|
||||
if isinstance(dst, MemoryOperand):
|
||||
is_store = mem == dst["memory"] or is_store
|
||||
return is_store
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ def find_marked_section(
|
||||
"immediate" in source
|
||||
and parser.normalize_imd(source.immediate) == mov_vals[0]
|
||||
and "register" in destination
|
||||
and parser.get_full_reg_name(destination['register']) == mov_reg
|
||||
and parser.get_full_reg_name(destination["register"]) == mov_reg
|
||||
):
|
||||
# operands of first instruction match start, check for second one
|
||||
match, line_count = match_bytes(lines, i + 1, nop_bytes)
|
||||
@@ -161,7 +161,7 @@ def find_marked_section(
|
||||
"immediate" in source
|
||||
and parser.normalize_imd(source.immediate) == mov_vals[1]
|
||||
and "register" in destination
|
||||
and parser.get_full_reg_name(destination['register']) == mov_reg
|
||||
and parser.get_full_reg_name(destination["register"]) == mov_reg
|
||||
):
|
||||
# operand of first instruction match end, check for second one
|
||||
match, line_count = match_bytes(lines, i + 1, nop_bytes)
|
||||
@@ -169,7 +169,8 @@ def find_marked_section(
|
||||
# return line of the marker
|
||||
index_end = i
|
||||
except TypeError:
|
||||
print(i, line)
|
||||
pass
|
||||
# print("TESTER",i, line)
|
||||
if index_start != -1 and index_end != -1:
|
||||
break
|
||||
return index_start, index_end
|
||||
|
||||
@@ -256,38 +256,38 @@ class TestSemanticTools(unittest.TestCase):
|
||||
def test_src_dst_assignment_x86(self):
|
||||
for instruction_form in self.kernel_x86:
|
||||
with self.subTest(instruction_form=instruction_form):
|
||||
if instruction_form["semantic_operands"] is not None:
|
||||
self.assertTrue("source" in instruction_form["semantic_operands"])
|
||||
self.assertTrue("destination" in instruction_form["semantic_operands"])
|
||||
self.assertTrue("src_dst" in instruction_form["semantic_operands"])
|
||||
if instruction_form.semantic_operands is not None:
|
||||
self.assertTrue("source" in instruction_form.semantic_operands)
|
||||
self.assertTrue("destination" in instruction_form.semantic_operands)
|
||||
self.assertTrue("src_dst" in instruction_form.semantic_operands)
|
||||
|
||||
def test_src_dst_assignment_AArch64(self):
|
||||
for instruction_form in self.kernel_AArch64:
|
||||
with self.subTest(instruction_form=instruction_form):
|
||||
if instruction_form["semantic_operands"] is not None:
|
||||
self.assertTrue("source" in instruction_form["semantic_operands"])
|
||||
self.assertTrue("destination" in instruction_form["semantic_operands"])
|
||||
self.assertTrue("src_dst" in instruction_form["semantic_operands"])
|
||||
if instruction_form.semantic_operands is not None:
|
||||
self.assertTrue("source" in instruction_form.semantic_operands)
|
||||
self.assertTrue("destination" in instruction_form.semantic_operands)
|
||||
self.assertTrue("src_dst" in instruction_form.semantic_operands)
|
||||
|
||||
def test_tp_lt_assignment_x86(self):
|
||||
self.assertTrue("ports" in self.machine_model_csx)
|
||||
port_num = len(self.machine_model_csx["ports"])
|
||||
for instruction_form in self.kernel_x86:
|
||||
with self.subTest(instruction_form=instruction_form):
|
||||
self.assertTrue("throughput" in instruction_form)
|
||||
self.assertTrue("latency" in instruction_form)
|
||||
self.assertIsInstance(instruction_form["port_pressure"], list)
|
||||
self.assertEqual(len(instruction_form["port_pressure"]), port_num)
|
||||
self.assertTrue(instruction_form.throughput != None)
|
||||
self.assertTrue(instruction_form.latency != None)
|
||||
self.assertIsInstance(instruction_form.port_pressure, list)
|
||||
self.assertEqual(len(instruction_form.port_pressure), port_num)
|
||||
|
||||
def test_tp_lt_assignment_AArch64(self):
|
||||
self.assertTrue("ports" in self.machine_model_tx2)
|
||||
port_num = len(self.machine_model_tx2["ports"])
|
||||
for instruction_form in self.kernel_AArch64:
|
||||
with self.subTest(instruction_form=instruction_form):
|
||||
self.assertTrue("throughput" in instruction_form)
|
||||
self.assertTrue("latency" in instruction_form)
|
||||
self.assertIsInstance(instruction_form["port_pressure"], list)
|
||||
self.assertEqual(len(instruction_form["port_pressure"]), port_num)
|
||||
self.assertTrue(instruction_form.throughput != None)
|
||||
self.assertTrue(instruction_form.latency != None)
|
||||
self.assertIsInstance(instruction_form.port_pressure, list)
|
||||
self.assertEqual(len(instruction_form.port_pressure), port_num)
|
||||
|
||||
def test_optimal_throughput_assignment(self):
|
||||
# x86
|
||||
|
||||
Reference in New Issue
Block a user