Added condition operand, adjusted tests to parse it & a few changes to get the kernelDG tests working

This commit is contained in:
stefandesouza
2023-10-29 16:36:00 +01:00
parent 14a2aa0b52
commit ebb973493b
20 changed files with 728 additions and 672 deletions

View File

@@ -11,9 +11,9 @@ import ruamel.yaml
from osaca.semantics import MachineModel
from osaca.parser import instructionForm
from osaca.parser.memory import memoryOperand
from osaca.parser.register import registerOperand
from osaca.parser.immediate import immediateOperand
from osaca.parser.memory import MemoryOperand
from osaca.parser.register import RegisterOperand
from osaca.parser.immediate import ImmediateOperand
def sanity_check(arch: str, verbose=False, internet_check=False, output_file=sys.stdout):
@@ -436,12 +436,12 @@ def _check_sanity_arch_db(arch_mm, isa_mm, internet_check=True):
# Check operands
for operand in instr_form["operands"]:
if isinstance(operand, registerOperand) and not (
if isinstance(operand, RegisterOperand) and not (
operand.name != None or operand.prefix != None
):
# Missing 'name' key
bad_operand.append(instr_form)
elif isinstance(operand, memoryOperand) and (
elif isinstance(operand, MemoryOperand) and (
operand.base is None
or operand.offset is None
or operand.index is None
@@ -449,7 +449,7 @@ def _check_sanity_arch_db(arch_mm, isa_mm, internet_check=True):
):
# Missing at least one key necessary for memory operands
bad_operand.append(instr_form)
elif isinstance(operand, immediateOperand) and operand.type == None:
elif isinstance(operand, ImmediateOperand) and operand.type == None:
# Missing 'imd' key
bad_operand.append(instr_form)
# every entry exists twice --> uniquify
@@ -611,7 +611,7 @@ 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"]:
if isinstance(op, registerOperand):
if isinstance(op, RegisterOperand):
op_attrs = []
if op.name != None:
op_attrs.append("name:" + op.name)

22
osaca/parser/condition.py Normal file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python3
from osaca.parser.operand import Operand
class ConditionOperand(Operand):
def __init__(
self,
ccode=None,
source=False,
destination=False,
):
super().__init__("condition", source, destination)
self._ccode = ccode
@property
def ccode(self):
return self._ccode
@ccode.setter
def ccode(self, ccode):
self._ccode = ccode

View File

@@ -3,7 +3,7 @@
from osaca.parser.operand import Operand
class directiveOperand(Operand):
class DirectiveOperand(Operand):
def __init__(self, name_id=None, parameter_id=None, comment_id=None):
super().__init__(name_id)
self._parameter_id = parameter_id
@@ -34,7 +34,7 @@ class directiveOperand(Operand):
self._comment_id = comment
def __eq__(self, other):
if isinstance(other, directiveOperand):
if isinstance(other, DirectiveOperand):
return (
self._name_id == other._name_id
and self._parameter_id == other._parameter_id
@@ -48,4 +48,4 @@ class directiveOperand(Operand):
return f"Directive(name_id={self._name_id}, parameters={self._parameter_id}, comment={self._comment_id})"
def __repr__(self):
return f"directiveOperand(name_id={self._name_id}, parameters={self._parameter_id}, comment={self._comment_id})"
return f"DirectiveOperand(name_id={self._name_id}, parameters={self._parameter_id}, comment={self._comment_id})"

View File

@@ -3,7 +3,7 @@
from osaca.parser.operand import Operand
class identifierOperand(Operand):
class IdentifierOperand(Operand):
def __init__(self, name=None, offset=None, relocation=None):
super().__init__(name)
self._offset = offset
@@ -27,8 +27,8 @@ class identifierOperand(Operand):
def __str__(self):
return (
f"identifierOperand({self.name}, offset={self.offset}, relocation={self.relocation})"
f"IdentifierOperand({self.name}, offset={self.offset}, relocation={self.relocation})"
)
def __repr__(self):
return f"identifierOperand(name={self.name}, offset={self.offset}, relocation={self.relocation})"
return f"IdentifierOperand(name={self.name}, offset={self.offset}, relocation={self.relocation})"

View File

@@ -3,7 +3,7 @@
from osaca.parser.operand import Operand
class immediateOperand(Operand):
class ImmediateOperand(Operand):
def __init__(
self,
identifier_id=None,
@@ -53,18 +53,18 @@ class immediateOperand(Operand):
def __str__(self):
return (
f"immediateOperand(identifier_id={self._identifier_id}, type_id={self._type_id}, "
f"ImmediateOperand(identifier_id={self._identifier_id}, type_id={self._type_id}, "
f"value_id={self._value_id}, shift_id={self._shift_id})"
)
def __repr__(self):
return (
f"immediateOperand(identifier_id={self._identifier_id}, type_id={self._type_id}, "
f"ImmediateOperand(identifier_id={self._identifier_id}, type_id={self._type_id}, "
f"value_id={self._value_id}, shift_id={self._shift_id})"
)
def __eq__(self, other):
if isinstance(other, immediateOperand):
if isinstance(other, ImmediateOperand):
return (
self._identifier_id == other._identifier_id
and self._type_id == other._type_id

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python3
from osaca.parser.directive import directiveOperand
from osaca.parser.directive import DirectiveOperand
class instructionForm:
@@ -179,11 +179,28 @@ class instructionForm:
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})"
def __str__(self):
return f"Instruction: {self._instruction_id}\nOperands: {self._operands_id}\nDirective: {self._directive_id}\nComment: {self._comment_id}\nLabel: {self._label_id}\nLine: {self._line}\nLine Number: {self._line_number}\nSemantic Operands: {self._semantic_operands}\nFlags: {self._flags}"
attributes = {
"instruction_id": self.instruction,
"operands_id": self.operands,
"hidden_operands": self.hidden_operands,
"directive_id": self.directive,
"comment_id": self.comment,
"label_id": self.label,
"line": self.line,
"line_number": self.line_number,
"semantic_operands": self.semantic_operands,
"throughput": self.throughput,
"latency": self.latency,
"uops": self.uops,
"port_pressure": self.port_pressure,
"breaks_dep": self.breaks_dep,
}
attr_str = "\n ".join(f"{key}={value}" for key, value in attributes.items())
return f"instructionForm({attr_str})"
def __repr__(self):
return self.__str__()
def __eq__(self, other):
if isinstance(other, instructionForm):

View File

@@ -3,7 +3,7 @@
from osaca.parser.operand import Operand
class labelOperand(Operand):
class LabelOperand(Operand):
def __init__(self, name_id=None, comment_id=None):
super().__init__(name_id)
self._comment_id = comment_id
@@ -25,7 +25,7 @@ class labelOperand(Operand):
return self._comment_id.pop(0)
def __str__(self):
return f"labelOperand(name_id={self._name_id}, comment={self._comment_id})"
return f"LabelOperand(name_id={self._name_id}, comment={self._comment_id})"
def __repr__(self):
return f"labelOperand(name_id={self._name_id}, comment={self._comment_id})"
return f"LabelOperand(name_id={self._name_id}, comment={self._comment_id})"

View File

@@ -3,7 +3,7 @@
from osaca.parser.operand import Operand
class memoryOperand(Operand):
class MemoryOperand(Operand):
def __init__(
self,
offset_ID=None,
@@ -127,7 +127,7 @@ class memoryOperand(Operand):
def __str__(self):
return (
f"memoryOperand(name_id={self._name_id}, offset_ID={self._offset_ID}, "
f"MemoryOperand(name_id={self._name_id}, offset_ID={self._offset_ID}, "
f"base_id={self._base_id}, index_id={self._index_id}, scale_id={self._scale_id}, "
f"segment_ext_id={self._segment_ext_id}, mask={self._mask}, "
f"pre_indexed={self._pre_indexed}, post_indexed={self._post_indexed}, "
@@ -137,7 +137,7 @@ class memoryOperand(Operand):
def __repr__(self):
return (
f"memoryOperand(name_id={self._name_id}, offset_ID={self._offset_ID}, "
f"MemoryOperand(name_id={self._name_id}, offset_ID={self._offset_ID}, "
f"base_id={self._base_id}, index_id={self._index_id}, scale_id={self._scale_id}, "
f"segment_ext_id={self._segment_ext_id}, mask={self._mask}, "
f"pre_indexed={self._pre_indexed}, post_indexed={self._post_indexed}, "
@@ -146,7 +146,7 @@ class memoryOperand(Operand):
)
def __eq__(self, other):
if isinstance(other, memoryOperand):
if isinstance(other, MemoryOperand):
return (
self._offset_ID == other._offset_ID
and self._base_id == other._base_id

View File

@@ -5,12 +5,12 @@ import pyparsing as pp
from osaca.parser import BaseParser
from osaca.parser.instruction_form import instructionForm
from osaca.parser.operand import Operand
from osaca.parser.directive import directiveOperand
from osaca.parser.memory import memoryOperand
from osaca.parser.label import labelOperand
from osaca.parser.register import registerOperand
from osaca.parser.identifier import identifierOperand
from osaca.parser.immediate import immediateOperand
from osaca.parser.directive import DirectiveOperand
from osaca.parser.memory import MemoryOperand
from osaca.parser.label import LabelOperand
from osaca.parser.register import RegisterOperand
from osaca.parser.identifier import IdentifierOperand
from osaca.parser.immediate import ImmediateOperand
class ParserAArch64(BaseParser):
@@ -301,7 +301,7 @@ class ParserAArch64(BaseParser):
result = self.process_operand(
self.directive.parseString(line, parseAll=True).asDict()
)
instruction_form.directive = directiveOperand(
instruction_form.directive = DirectiveOperand(
name_id=result.name, parameter_id=result.parameters
)
if result.comment is not None:
@@ -383,7 +383,7 @@ class ParserAArch64(BaseParser):
if self.REGISTER_ID in operand:
return self.process_register_operand(operand[self.REGISTER_ID])
if self.directive_id in operand:
return directiveOperand(
return DirectiveOperand(
name_id=operand["directive"]["name"],
parameter_id=operand["directive"]["parameters"],
comment_id=operand["directive"]["comment"]
@@ -393,7 +393,7 @@ class ParserAArch64(BaseParser):
return operand
def process_register_operand(self, operand):
return registerOperand(
return RegisterOperand(
prefix_id=operand["prefix"],
name_id=operand["name"],
shape=operand["shape"] if "shape" in operand else None,
@@ -422,9 +422,9 @@ class ParserAArch64(BaseParser):
if "shift" in memory_address["index"]:
if memory_address["index"]["shift_op"].lower() in valid_shift_ops:
scale = 2 ** int(memory_address["index"]["shift"][0]["value"])
new_dict = memoryOperand(
new_dict = MemoryOperand(
offset_ID=offset,
base_id=registerOperand(name_id=base["name"], prefix_id=base["prefix"]),
base_id=RegisterOperand(name_id=base["name"], prefix_id=base["prefix"]),
index_id=index,
scale_id=scale,
)
@@ -440,7 +440,7 @@ class ParserAArch64(BaseParser):
def process_sp_register(self, register):
"""Post-process stack pointer register"""
# reg = register
new_reg = registerOperand(prefix_id="x", name_id="sp")
new_reg = RegisterOperand(prefix_id="x", name_id="sp")
# reg["prefix"] = "x"
return new_reg
@@ -509,7 +509,7 @@ class ParserAArch64(BaseParser):
immediate["type"] = "int"
# convert hex/bin immediates to dec
immediate["value"] = self.normalize_imd(immediate)
return immediateOperand(type_id=immediate["type"], value_id=immediate["value"])
return ImmediateOperand(type_id=immediate["type"], value_id=immediate["value"])
if "base_immediate" in immediate:
# arithmetic immediate, add calculated value as value
immediate["shift"] = immediate["shift"][0]
@@ -517,7 +517,7 @@ class ParserAArch64(BaseParser):
immediate["shift"]["value"]
)
immediate["type"] = "int"
return immediateOperand(
return ImmediateOperand(
type_id=immediate["type"], value_id=immediate["value"], shift_id=immediate["shift"]
)
if "float" in immediate:
@@ -526,16 +526,16 @@ class ParserAArch64(BaseParser):
dict_name = "double"
if "exponent" in immediate[dict_name]:
immediate["type"] = dict_name
return immediateOperand(type_id=immediate["type"])
return ImmediateOperand(type_id=immediate["type"])
else:
# change 'mantissa' key to 'value'
return immediateOperand(value_id=immediate[dict_name]["mantissa"], type_id=dict_name)
return ImmediateOperand(value_id=immediate[dict_name]["mantissa"], type_id=dict_name)
def process_label(self, label):
"""Post-process label asm line"""
# remove duplicated 'name' level due to identifier
# label["name"] = label["name"]["name"]
new_label = labelOperand(
new_label = LabelOperand(
name_id=label["name"]["name"],
comment_id=label["comment"] if self.comment_id in label else None,
)
@@ -546,7 +546,7 @@ class ParserAArch64(BaseParser):
# remove value if it consists of symbol+offset
if "value" in identifier:
del identifier["value"]
return identifierOperand(offset=identifier["offset"], RELOCATION=identifier["relocation"])
return IdentifierOperand(offset=identifier["offset"], RELOCATION=identifier["relocation"])
def get_full_reg_name(self, register):
"""Return one register name string including all attributes"""

View File

@@ -8,12 +8,12 @@ import pyparsing as pp
from osaca.parser import BaseParser
from osaca.parser.instruction_form import instructionForm
from osaca.parser.operand import Operand
from osaca.parser.directive import directiveOperand
from osaca.parser.memory import memoryOperand
from osaca.parser.label import labelOperand
from osaca.parser.register import registerOperand
from osaca.parser.identifier import identifierOperand
from osaca.parser.immediate import immediateOperand
from osaca.parser.directive import DirectiveOperand
from osaca.parser.memory import MemoryOperand
from osaca.parser.label import LabelOperand
from osaca.parser.register import RegisterOperand
from osaca.parser.identifier import IdentifierOperand
from osaca.parser.immediate import ImmediateOperand
class ParserX86ATT(BaseParser):
@@ -233,7 +233,7 @@ class ParserX86ATT(BaseParser):
result = self.process_operand(
self.directive.parseString(line, parseAll=True).asDict()
)
instruction_form.directive = directiveOperand(
instruction_form.directive = DirectiveOperand(
name_id=result.name,
parameter_id=result.parameters,
)
@@ -299,7 +299,7 @@ class ParserX86ATT(BaseParser):
if self.directive_id in operand:
return self.process_directive(operand[self.directive_id])
if self.REGISTER_ID in operand:
return registerOperand(
return RegisterOperand(
prefix_id=operand["register"]["prefix"]
if "prefix" in operand["register"]
else None,
@@ -314,7 +314,7 @@ class ParserX86ATT(BaseParser):
return operand
def process_directive(self, directive):
directive_new = directiveOperand(name_id=directive["name"], parameter_id=[])
directive_new = DirectiveOperand(name_id=directive["name"], parameter_id=[])
if "parameters" in directive:
directive_new.parameters = directive["parameters"]
if "comment" in directive:
@@ -338,14 +338,14 @@ class ParserX86ATT(BaseParser):
elif offset is not None and "value" in offset:
offset["value"] = int(offset["value"], 0)
if base != None:
baseOp = registerOperand(
baseOp = RegisterOperand(
name_id=base["name"], prefix_id=base["prefix"] if "prefix" in base else None
)
if index != None:
indexOp = registerOperand(
indexOp = RegisterOperand(
name_id=index["name"], prefix_id=index["prefix"] if "prefix" in index else None
)
new_dict = memoryOperand(
new_dict = MemoryOperand(
offset_ID=offset, base_id=baseOp, index_id=indexOp, scale_id=scale
)
# Add segmentation extension if existing
@@ -357,7 +357,7 @@ class ParserX86ATT(BaseParser):
"""Post-process label asm line"""
# remove duplicated 'name' level due to identifier
label["name"] = label["name"][0]["name"]
new_label = labelOperand(
new_label = LabelOperand(
name_id=label["name"], comment_id=label["comment"] if "comment" in label else None
)
return new_label

View File

@@ -3,7 +3,7 @@
from osaca.parser.operand import Operand
class registerOperand(Operand):
class RegisterOperand(Operand):
def __init__(
self,
name_id=None,
@@ -114,7 +114,7 @@ class registerOperand(Operand):
def __str__(self):
return (
f"registerOperand(name_id={self._name_id}, width_id={self._width_id}, "
f"RegisterOperand(name_id={self._name_id}, width_id={self._width_id}, "
f"prefix_id={self._prefix_id}, reg_id={self._reg_id}, REGtype_id={self._regtype_id}, "
f"lanes={self._lanes}, shape={self._shape}, index={self._index}, "
f"mask={self._mask}, zeroing={self._zeroing})"
@@ -122,14 +122,14 @@ class registerOperand(Operand):
def __repr__(self):
return (
f"registerOperand(name_id={self._name_id}, width_id={self._width_id}, "
f"RegisterOperand(name_id={self._name_id}, width_id={self._width_id}, "
f"prefix_id={self._prefix_id}, reg_id={self._reg_id}, REGtype_id={self._regtype_id}, "
f"lanes={self._lanes}, shape={self._shape}, index={self._index}, "
f"mask={self._mask}, zeroing={self._zeroing})"
)
def __eq__(self, other):
if isinstance(other, registerOperand):
if isinstance(other, RegisterOperand):
return (
self._name_id == other._name_id
and self._width_id == other._width_id

View File

@@ -9,10 +9,10 @@ from copy import deepcopy
from .hw_model import MachineModel
from .isa_semantics import INSTR_flags, ISASemantics
from osaca.parser.memory import memoryOperand
from osaca.parser.register import registerOperand
from osaca.parser.immediate import immediateOperand
from osaca.parser.identifier import identifierOperand
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 ArchSemantics(ISASemantics):
@@ -55,9 +55,9 @@ class ArchSemantics(ISASemantics):
best_kernel_tp = sys.maxsize
for port_util_alt in list(instruction_form.port_uops.values())[1:]:
k_tmp = deepcopy(kernel)
k_tmp[idx]["port_uops"] = deepcopy(port_util_alt)
k_tmp[idx]["port_pressure"] = self._machine_model.average_port_pressure(
k_tmp[idx]["port_uops"]
k_tmp[idx].port_uops = deepcopy(port_util_alt)
k_tmp[idx].port_pressure = self._machine_model.average_port_pressure(
k_tmp[idx].port_uops
)
k_tmp.reverse()
self.assign_optimal_throughput(k_tmp, idx)
@@ -66,7 +66,7 @@ class ArchSemantics(ISASemantics):
best_kernel_tp = max(self.get_throughput_sum(best_kernel))
# check the first option in the main branch and compare against the best option later
multiple_assignments = True
kernel[idx]["port_uops"] = list(instruction_form.port_uops.values())[0]
kernel[idx].port_uops = list(instruction_form.port_uops.values())[0]
for uop in instruction_form.port_uops:
cycles = uop[0]
ports = list(uop[1])
@@ -134,8 +134,8 @@ class ArchSemantics(ISASemantics):
if multiple_assignments:
if max(self.get_throughput_sum(kernel)) > best_kernel_tp:
for i, instr in enumerate(best_kernel):
kernel[i]["port_uops"] = best_kernel[i]["port_uops"]
kernel[i]["port_pressure"] = best_kernel[i]["port_pressure"]
kernel[i].port_uops = best_kernel[i].port_uops
kernel[i].port_pressure = best_kernel[i].port_pressure
def set_hidden_loads(self, kernel):
"""Hide loads behind stores if architecture supports hidden loads (depricated)"""
@@ -262,7 +262,7 @@ class ArchSemantics(ISASemantics):
]
)
# dummy_reg = {"class": "register", "name": reg_type}
dummy_reg = registerOperand(name_id=reg_type)
dummy_reg = RegisterOperand(name_id=reg_type)
data_port_pressure = [0.0 for _ in range(port_number)]
data_port_uops = []
if INSTR_flags.HAS_LD in instruction_form.flags:
@@ -272,7 +272,7 @@ class ArchSemantics(ISASemantics):
x
for x in instruction_form.semantic_operands["source"]
+ instruction_form.semantic_operands["src_dst"]
if isinstance(x, memoryOperand)
if isinstance(x, MemoryOperand)
][0]
)
# if multiple options, choose based on reg type
@@ -281,7 +281,7 @@ class ArchSemantics(ISASemantics):
for ldp in load_perf_data
if ldp.dst != None
and self._machine_model._check_operands(
dummy_reg, registerOperand(name_id=ldp.dst)
dummy_reg, RegisterOperand(name_id=ldp.dst)
)
]
if len(data_port_uops) < 1:
@@ -303,7 +303,7 @@ class ArchSemantics(ISASemantics):
+ instruction_form.semantic_operands["src_dst"]
)
store_perf_data = self._machine_model.get_store_throughput(
[x for x in destinations if isinstance(x, memoryOperand)][0],
[x for x in destinations if isinstance(x, MemoryOperand)][0],
dummy_reg,
)
st_data_port_uops = store_perf_data[0].port_pressure
@@ -320,7 +320,7 @@ class ArchSemantics(ISASemantics):
[
op.post_indexed or op.pre_indexed
for op in instruction_form.semantic_operands["src_dst"]
if isinstance(op, memoryOperand)
if isinstance(op, MemoryOperand)
]
)
):
@@ -444,11 +444,11 @@ class ArchSemantics(ISASemantics):
"""Create register operand for a memory addressing operand"""
if self._isa == "x86":
if reg_type == "gpr":
register = registerOperand(name_id="r" + str(int(reg_id) + 9))
register = RegisterOperand(name_id="r" + str(int(reg_id) + 9))
else:
register = registerOperand(name_id=reg_type + reg_id)
register = RegisterOperand(name_id=reg_type + reg_id)
elif self._isa == "aarch64":
register = registerOperand(name_id=reg_id, prefix_id=reg_type)
register = RegisterOperand(name_id=reg_id, prefix_id=reg_type)
return register
def _nullify_data_ports(self, port_pressure):

View File

@@ -16,10 +16,11 @@ from osaca.parser import ParserX86ATT
from ruamel.yaml.compat import StringIO
from osaca.parser.instruction_form import instructionForm
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
from osaca.parser.memory import MemoryOperand
from osaca.parser.register import RegisterOperand
from osaca.parser.immediate import ImmediateOperand
from osaca.parser.identifier import IdentifierOperand
from osaca.parser.condition import ConditionOperand
class MachineModel(object):
@@ -144,7 +145,7 @@ class MachineModel(object):
# List containing classes with same name/instruction
self._data["instruction_forms_dict"][iform["name"]].append(new_iform)
self._data["internal_version"] = self.INTERNAL_VERSION
self.load_store_tp()
if not lazy:
# cache internal representation for future use
self._write_in_cache(self._path)
@@ -157,13 +158,13 @@ class MachineModel(object):
if "load_throughput" in self._data:
for m in self._data["load_throughput"]:
new_throughputs.append(
memoryOperand(
MemoryOperand(
base_id=m["base"],
offset_ID=m["offset"],
scale_id=m["scale"],
index_id=m["index"],
port_pressure=m["port_pressure"],
ds=m["dst"] if "dst" in m else None,
dst=m["dst"] if "dst" in m else None,
)
)
self._data["load_throughput"] = new_throughputs
@@ -172,7 +173,7 @@ class MachineModel(object):
if "store_throughput" in self._data:
for m in self._data["store_throughput"]:
new_throughputs.append(
memoryOperand(
MemoryOperand(
base_id=m["base"],
offset_ID=m["offset"],
scale_id=m["scale"],
@@ -186,7 +187,7 @@ class MachineModel(object):
"""Convert an operand from dict type to class"""
if o["class"] == "register":
new_operands.append(
registerOperand(
RegisterOperand(
name_id=o["name"] if "name" in o else None,
prefix_id=o["prefix"] if "prefix" in o else None,
shape=o["shape"] if "shape" in o else None,
@@ -197,7 +198,7 @@ class MachineModel(object):
)
elif o["class"] == "memory":
new_operands.append(
memoryOperand(
MemoryOperand(
base_id=o["base"],
offset_ID=o["offset"],
index_id=o["index"],
@@ -208,14 +209,22 @@ class MachineModel(object):
)
elif o["class"] == "immediate":
new_operands.append(
immediateOperand(
ImmediateOperand(
type_id=o["imd"],
source=o["source"] if "source" in o else False,
destination=o["destination"] if "destination" in o else False,
)
)
elif o["class"] == "identifier":
new_operands.append(identifierOperand())
new_operands.append(IdentifierOperand())
elif o["class"] == "condition":
new_operands.append(
ConditionOperand(
ccode=o["ccode"],
source=o["source"] if "source" in o else False,
destination=o["destination"] if "destination" in o else False,
)
)
else:
new_operands.append(o)
@@ -340,7 +349,7 @@ class MachineModel(object):
ld_tp = [m for m in self._data["load_throughput"] if self._match_mem_entries(memory, m)]
if len(ld_tp) > 0:
return ld_tp.copy()
return [memoryOperand(port_pressure=self._data["load_throughput_default"].copy())]
return [MemoryOperand(port_pressure=self._data["load_throughput_default"].copy())]
def get_store_latency(self, reg_type):
"""Return store latency for given register type."""
@@ -355,11 +364,11 @@ class MachineModel(object):
tp
for tp in st_tp
if "src" in tp
and self._check_operands(src_reg, registerOperand(name_id=tp["src"]))
and self._check_operands(src_reg, RegisterOperand(name_id=tp["src"]))
]
if len(st_tp) > 0:
return st_tp.copy()
return [memoryOperand(port_pressure=self._data["store_throughput_default"].copy())]
return [MemoryOperand(port_pressure=self._data["store_throughput_default"].copy())]
def _match_mem_entries(self, mem, i_mem):
"""Check if memory addressing ``mem`` and ``i_mem`` are of the same type."""
@@ -378,7 +387,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 = []
if op.name != None:
op_attrs.append("name:" + op.name)
@@ -387,7 +396,7 @@ class MachineModel(object):
if op.shape != None:
op_attrs.append("shape:" + op.shape)
operands.append("{}({})".format("register", ",".join(op_attrs)))
return "{} {}".format(instruction_form["name"].lower(), ",".join(operands))
return "{} {}".format(instruction_form.instruction.lower(), ",".join(operands))
@staticmethod
def get_isa_for_arch(arch):
@@ -571,13 +580,13 @@ class MachineModel(object):
def _create_db_operand_aarch64(self, operand):
"""Create instruction form operand for DB out of operand string."""
if operand == "i":
return immediateOperand(type_id="int")
return ImmediateOperand(type_id="int")
elif operand in "wxbhsdq":
return registerOperand(prefix_id=operand)
return RegisterOperand(prefix_id=operand)
elif operand.startswith("v"):
return registerOperand(prefix_id="v", shape=operand[1:2])
return RegisterOperand(prefix_id="v", shape=operand[1:2])
elif operand.startswith("m"):
return memoryOperand(
return MemoryOperand(
base_id="x" if "b" in operand else None,
offset_ID="imd" if "o" in operand else None,
index_id="gpr" if "i" in operand else None,
@@ -591,13 +600,13 @@ class MachineModel(object):
def _create_db_operand_x86(self, operand):
"""Create instruction form operand for DB out of operand string."""
if operand == "r":
return registerOperand(name_id="gpr")
return RegisterOperand(name_id="gpr")
elif operand in "xyz":
return registerOperand(name_id=operand + "mm")
return RegisterOperand(name_id=operand + "mm")
elif operand == "i":
return immediateOperand(type_id="int")
return ImmediateOperand(type_id="int")
elif operand.startswith("m"):
return memoryOperand(
return MemoryOperand(
base_id="gpr" if "b" in operand else None,
offset_ID="imd" if "o" in operand else None,
index_id="gpr" if "i" in operand else None,
@@ -644,7 +653,7 @@ class MachineModel(object):
if (isinstance(operand, Operand) and operand.name == self.WILDCARD) or (
not isinstance(operand, Operand) and self.WILDCARD in operand
):
if isinstance(i_operand, registerOperand):
if isinstance(i_operand, RegisterOperand):
return True
else:
return False
@@ -660,57 +669,52 @@ class MachineModel(object):
# return self._compare_db_entries(i_operand, operand)
# TODO support class wildcards
# register
if isinstance(operand, registerOperand):
if not isinstance(i_operand, registerOperand):
if isinstance(operand, RegisterOperand):
if not isinstance(i_operand, RegisterOperand):
return False
return self._is_AArch64_reg_type(i_operand, operand)
# memory
if isinstance(operand, memoryOperand):
if not isinstance(i_operand, memoryOperand):
if isinstance(operand, MemoryOperand):
if not isinstance(i_operand, MemoryOperand):
return False
return self._is_AArch64_mem_type(i_operand, operand)
# immediate
if isinstance(i_operand, immediateOperand) and i_operand.type == self.WILDCARD:
return isinstance(operand, immediateOperand) and (operand.value != None)
if isinstance(i_operand, ImmediateOperand) and i_operand.type == self.WILDCARD:
return isinstance(operand, ImmediateOperand) and (operand.value != None)
if isinstance(i_operand, immediateOperand) and i_operand.type == "int":
if isinstance(i_operand, ImmediateOperand) and i_operand.type == "int":
return (
isinstance(operand, immediateOperand)
isinstance(operand, ImmediateOperand)
and operand.type == "int"
and operand.value != None
)
if isinstance(i_operand, immediateOperand) and i_operand.type == "float":
if isinstance(i_operand, ImmediateOperand) and i_operand.type == "float":
return (
isinstance(operand, immediateOperand)
isinstance(operand, ImmediateOperand)
and operand.type == "float"
and operand.value != None
)
if isinstance(i_operand, immediateOperand) and i_operand.type == "double":
if isinstance(i_operand, ImmediateOperand) and i_operand.type == "double":
return (
isinstance(operand, immediateOperand)
isinstance(operand, ImmediateOperand)
and operand.type == "double"
and operand.value != None
)
# identifier
if isinstance(operand, identifierOperand) or (
isinstance(operand, immediateOperand) and operand.identifier != None
if isinstance(operand, IdentifierOperand) or (
isinstance(operand, ImmediateOperand) and operand.identifier != None
):
return isinstance(i_operand, identifierOperand)
return isinstance(i_operand, IdentifierOperand)
# prefetch option
if not isinstance(operand, Operand) and "prfop" in operand:
return i_operand["class"] == "prfop"
# condition
if not isinstance(operand, Operand) and "condition" in operand:
if i_operand["ccode"] == self.WILDCARD:
return True
return i_operand["class"] == "condition" and (
operand.get("condition", None) == i_operand.get("ccode", None).upper()
if isinstance(i_operand.get("ccode", None), str)
else i_operand.get("ccode", None)
)
if isinstance(operand, ConditionOperand):
if isinstance(i_operand, ConditionOperand):
return (i_operand.ccode == self.WILDCARD) or (i_operand.ccode == operand.ccode)
# no match
return False
@@ -720,22 +724,22 @@ class MachineModel(object):
# compare two DB entries
# return self._compare_db_entries(i_operand, operand)
# register
if isinstance(operand, registerOperand):
if not isinstance(i_operand, registerOperand):
if isinstance(operand, RegisterOperand):
if not isinstance(i_operand, RegisterOperand):
return False
return self._is_x86_reg_type(i_operand, operand, consider_masking=False)
# memory
if isinstance(operand, memoryOperand):
if not isinstance(i_operand, memoryOperand):
if isinstance(operand, MemoryOperand):
if not isinstance(i_operand, MemoryOperand):
return False
return self._is_x86_mem_type(i_operand, operand)
# immediate
if isinstance(operand, immediateOperand):
if isinstance(operand, ImmediateOperand):
# if "immediate" in operand.name or operand.value != None:
return isinstance(i_operand, immediateOperand) and i_operand.type == "int"
return isinstance(i_operand, ImmediateOperand) and i_operand.type == "int"
# identifier (e.g., labels)
if isinstance(operand, identifierOperand):
return isinstance(i_operand, identifierOperand)
if isinstance(operand, IdentifierOperand):
return isinstance(i_operand, IdentifierOperand)
return self._compare_db_entries(i_operand, operand)
def _compare_db_entries(self, operand_1, operand_2):
@@ -791,7 +795,7 @@ class MachineModel(object):
if i_reg is None:
return True
return False
if isinstance(i_reg, registerOperand):
if isinstance(i_reg, RegisterOperand):
i_reg_name = i_reg.name
else:
i_reg_name = i_reg
@@ -843,7 +847,7 @@ class MachineModel(object):
(
(mem.base is None and i_mem.base is None)
or i_mem.base == self.WILDCARD
or (isinstance(mem.base, registerOperand) and (mem.base.prefix == i_mem.base))
or (isinstance(mem.base, RegisterOperand) and (mem.base.prefix == i_mem.base))
)
# check offset
and (

View File

@@ -3,9 +3,9 @@ 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 osaca.parser.immediate import immediateOperand
from osaca.parser.memory import MemoryOperand
from osaca.parser.register import RegisterOperand
from osaca.parser.immediate import ImmediateOperand
from .hw_model import MachineModel
@@ -81,7 +81,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 any([isinstance(op, memoryOperand) for op in operands]):
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
@@ -116,7 +116,7 @@ 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 isinstance(op, memoryOperand)]:
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:
@@ -127,7 +127,7 @@ class ISASemantics(object):
"post_indexed": post_indexed,
}
)
for operand in [op for op in op_dict["destination"] if isinstance(op, memoryOperand)]:
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:
@@ -165,7 +165,7 @@ class ISASemantics(object):
instruction_form.semantic_operands["destination"],
instruction_form.semantic_operands["src_dst"],
)
if isinstance(op, registerOperand)
if isinstance(op, RegisterOperand)
]
isa_data = self._isa_model.get_instruction(
instruction_form.instruction, instruction_form.operands
@@ -188,7 +188,7 @@ class ISASemantics(object):
if only_postindexed:
for o in instruction_form.operands:
if isinstance(o, memoryOperand) and o.base != None and o.post_indexed != False:
if isinstance(o, MemoryOperand) and o.base != None and o.post_indexed != False:
base_name = o.base.prefix if o.base.prefix != None else "" + o.base.name
return {
base_name: {
@@ -202,7 +202,7 @@ class ISASemantics(object):
operand_state = {} # e.g., {'op1': {'name': 'rax', 'value': 0}} 0 means unchanged
for o in instruction_form.operands:
if isinstance(o, memoryOperand) and o.pre_indexed:
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(
@@ -216,13 +216,13 @@ class ISASemantics(object):
if isa_data is not None:
for i, o in enumerate(instruction_form.operands):
operand_name = "op{}".format(i + 1)
if isinstance(o, registerOperand):
if isinstance(o, RegisterOperand):
o_reg_name = o.prefix if o.prefix != None else "" + o.name
reg_operand_names[o_reg_name] = operand_name
operand_state[operand_name] = {"name": o_reg_name, "value": 0}
elif isinstance(o, immediateOperand):
elif isinstance(o, ImmediateOperand):
operand_state[operand_name] = {"value": o.value}
elif isinstance(o, memoryOperand):
elif isinstance(o, MemoryOperand):
# TODO lea needs some thinking about
pass
@@ -253,12 +253,12 @@ class ISASemantics(object):
# handle dependency breaking instructions
"""
if "breaks_dependency_on_equal_operands" in isa_data and operands[1:] == operands[:-1]:
if isa_data.breaks_dep and operands[1:] == operands[:-1]:
op_dict["destination"] += operands
if "hidden_operands" in isa_data:
if isa_data.hidden_operands!=[]:
op_dict["destination"] += [
{hop["class"]: {k: hop[k] for k in ["name", "class", "source", "destination"]}}
for hop in isa_data["hidden_operands"]
for hop in isa_data.hidden_operands
]
return op_dict
"""
@@ -276,21 +276,20 @@ class ISASemantics(object):
# check for hidden operands like flags or registers
"""
if "hidden_operands" in isa_data:
if isa_data.hidden_operands!=[]:
# add operand(s) to semantic_operands of instruction form
for op in isa_data["hidden_operands"]:
for op in isa_data.hidden_operands:
dict_key = (
"src_dst"
if op["source"] and op["destination"]
if op.source and op.destination
else "source"
if op["source"]
if op.source
else "destination"
)
hidden_op = {op["class"]: {}}
key_filter = ["class", "source", "destination"]
for key in [k for k in op.keys() if k not in key_filter]:
hidden_op[op["class"]][key] = op[key]
hidden_op = AttrDict.convert_dict(hidden_op)
op_dict[dict_key].append(hidden_op)
"""
return op_dict
@@ -301,7 +300,7 @@ class ISASemantics(object):
instruction_form.semantic_operands["source"],
instruction_form.semantic_operands["src_dst"],
):
if isinstance(operand, memoryOperand):
if isinstance(operand, MemoryOperand):
return True
return False
@@ -311,7 +310,7 @@ class ISASemantics(object):
instruction_form.semantic_operands["destination"],
instruction_form.semantic_operands["src_dst"],
):
if isinstance(operand, memoryOperand):
if isinstance(operand, MemoryOperand):
return True
return False
@@ -345,7 +344,7 @@ class ISASemantics(object):
def substitute_mem_address(self, operands):
"""Create memory wildcard for all memory operands"""
return [
self._create_reg_wildcard() if isinstance(op, memoryOperand) else op for op in operands
self._create_reg_wildcard() if isinstance(op, MemoryOperand) else op for op in operands
]
def _create_reg_wildcard(self):

View File

@@ -9,9 +9,9 @@ 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
from osaca.parser.immediate import immediateOperand
from osaca.parser.memory import MemoryOperand
from osaca.parser.register import RegisterOperand
from osaca.parser.immediate import ImmediateOperand
class KernelDG(nx.DiGraph):
@@ -283,7 +283,7 @@ 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 isinstance(dst, registerOperand):
if isinstance(dst, RegisterOperand):
# read of register
if self.is_read(dst, instr_form):
# if dst.pre_indexed or dst.post_indexed:
@@ -294,8 +294,8 @@ class KernelDG(nx.DiGraph):
if self.is_written(dst, instr_form):
break
if (
not isinstance(dst, registerOperand)
and not isinstance(dst, memoryOperand)
not isinstance(dst, RegisterOperand)
and not isinstance(dst, MemoryOperand)
and "flag" in dst
and flag_dependencies
):
@@ -305,7 +305,7 @@ class KernelDG(nx.DiGraph):
# write to flag -> abort
if self.is_written(dst.flag, instr_form):
break
if isinstance(dst, memoryOperand):
if isinstance(dst, MemoryOperand):
# base register is altered during memory access
if dst.pre_indexed != None:
if self.is_written(dst.base, instr_form):
@@ -374,16 +374,16 @@ class KernelDG(nx.DiGraph):
instruction_form.semantic_operands["source"],
instruction_form.semantic_operands["src_dst"],
):
if isinstance(src, registerOperand):
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 not isinstance(src, immediateOperand)
not isinstance(src, RegisterOperand)
and not isinstance(src, MemoryOperand)
and not isinstance(src, ImmediateOperand)
and "flag" in src
):
is_read = self.parser.is_flag_dependend_of(register, src.flag) or is_read
if isinstance(src, memoryOperand):
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:
@@ -393,7 +393,7 @@ class KernelDG(nx.DiGraph):
instruction_form.semantic_operands["destination"],
instruction_form.semantic_operands["src_dst"],
):
if isinstance(dst, memoryOperand):
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:
@@ -409,7 +409,7 @@ class KernelDG(nx.DiGraph):
instruction_form.semantic_operands["src_dst"],
):
# Here we check for mem dependecies only
if not isinstance(src, memoryOperand):
if not isinstance(src, MemoryOperand):
continue
# src = src.memory
@@ -482,15 +482,15 @@ class KernelDG(nx.DiGraph):
instruction_form.semantic_operands["destination"],
instruction_form.semantic_operands["src_dst"],
):
if isinstance(dst, registerOperand):
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)
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 isinstance(dst, memoryOperand):
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
@@ -498,7 +498,7 @@ class KernelDG(nx.DiGraph):
instruction_form.semantic_operands["source"],
instruction_form.semantic_operands["src_dst"],
):
if isinstance(src, memoryOperand):
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
@@ -512,7 +512,7 @@ class KernelDG(nx.DiGraph):
instruction_form.semantic_operands["destination"],
instruction_form.semantic_operands["src_dst"],
):
if isinstance(dst, memoryOperand):
if isinstance(dst, MemoryOperand):
is_store = mem == dst or is_store
return is_store
@@ -526,11 +526,11 @@ class KernelDG(nx.DiGraph):
"""
graph = copy.deepcopy(self.dg)
cp = self.get_critical_path()
cp_line_numbers = [x["line_number"] for x in cp]
cp_line_numbers = [x.line_number for x in cp]
lcd = self.get_loopcarried_dependencies()
lcd_line_numbers = {}
for dep in lcd:
lcd_line_numbers[dep] = [x["line_number"] for x, lat in lcd[dep]["dependencies"]]
lcd_line_numbers[dep] = [x.line_number for x, lat in lcd[dep]["dependencies"]]
# add color scheme
graph.graph["node"] = {"colorscheme": "accent8"}
graph.graph["edge"] = {"colorscheme": "accent8"}
@@ -541,7 +541,7 @@ class KernelDG(nx.DiGraph):
max_line_number = max(lcd_line_numbers[dep])
graph.add_edge(max_line_number, min_line_number)
graph.edges[max_line_number, min_line_number]["latency"] = [
lat for x, lat in lcd[dep]["dependencies"] if x["line_number"] == max_line_number
lat for x, lat in lcd[dep]["dependencies"] if x.line_number == max_line_number
]
# add label to edges
@@ -550,7 +550,7 @@ class KernelDG(nx.DiGraph):
# add CP values to graph
for n in cp:
graph.nodes[n["line_number"]]["instruction_form"]["latency_cp"] = n["latency_cp"]
graph.nodes[n.line_number]["instruction_form"].latency_cp = n.latency_cp
# color CP and LCD
for n in graph.nodes:
@@ -569,8 +569,8 @@ class KernelDG(nx.DiGraph):
# color edges
for e in graph.edges:
if (
graph.nodes[e[0]]["instruction_form"]["line_number"] in cp_line_numbers
and graph.nodes[e[1]]["instruction_form"]["line_number"] in cp_line_numbers
graph.nodes[e[0]]["instruction_form"].line_number in cp_line_numbers
and graph.nodes[e[1]]["instruction_form"].line_number in cp_line_numbers
and e[0] < e[1]
):
bold_edge = True
@@ -582,9 +582,8 @@ class KernelDG(nx.DiGraph):
graph.edges[e]["penwidth"] = 3
for dep in lcd_line_numbers:
if (
graph.nodes[e[0]]["instruction_form"]["line_number"] in lcd_line_numbers[dep]
and graph.nodes[e[1]]["instruction_form"]["line_number"]
in lcd_line_numbers[dep]
graph.nodes[e[0]]["instruction_form"].line_number in lcd_line_numbers[dep]
and graph.nodes[e[1]]["instruction_form"].line_number in lcd_line_numbers[dep]
):
graph.edges[e]["color"] = graph.nodes[e[1]]["fillcolor"]
@@ -597,12 +596,12 @@ class KernelDG(nx.DiGraph):
graph.nodes[n]["fontsize"] = 11.0
else:
node = graph.nodes[n]["instruction_form"]
if node["instruction"] is not None:
mapping[n] = "{}: {}".format(n, node["instruction"])
if node.instruction is not None:
mapping[n] = "{}: {}".format(n, node.instruction)
else:
label = "label" if node["label"] else None
label = "directive" if node["directive"] else label
label = "comment" if node["comment"] and label is None else label
label = "label" if node.label != None else None
label = "directive" if node.directive != None else label
label = "comment" if node.comment != None and label is None else label
mapping[n] = "{}: {}".format(n, label)
graph.nodes[n]["fontname"] = "italic"
graph.nodes[n]["fontsize"] = 11.0

View File

@@ -10,8 +10,8 @@ import osaca.db_interface as dbi
from osaca.db_interface import sanity_check
from osaca.semantics import MachineModel
from osaca.parser import instructionForm
from osaca.parser.memory import memoryOperand
from osaca.parser.register import registerOperand
from osaca.parser.memory import MemoryOperand
from osaca.parser.register import RegisterOperand
import copy
@@ -21,8 +21,8 @@ class TestDBInterface(unittest.TestCase):
sample_entry = instructionForm(
instruction_id="DoItRightAndDoItFast",
operands_id=[
memoryOperand(offset_ID="imd", base_id="gpr", index_id="gpr", scale_id=8),
registerOperand(name_id="xmm"),
MemoryOperand(offset_ID="imd", base_id="gpr", index_id="gpr", scale_id=8),
RegisterOperand(name_id="xmm"),
],
throughput=1.25,
latency=125,

View File

@@ -9,6 +9,7 @@ import unittest
from osaca.frontend import Frontend
from osaca.parser import ParserAArch64, ParserX86ATT
from osaca.semantics import ArchSemantics, KernelDG, MachineModel, reduce_to_section
from osaca.parser.operand import Operand
class TestFrontend(unittest.TestCase):
@@ -41,6 +42,7 @@ class TestFrontend(unittest.TestCase):
self.machine_model_tx2,
path_to_yaml=os.path.join(self.MODULE_DATA_DIR, "isa/aarch64.yml"),
)
for i in range(len(self.kernel_x86)):
self.semantics_csx.assign_src_dst(self.kernel_x86[i])
self.semantics_csx.assign_tp_lt(self.kernel_x86[i])

View File

@@ -10,10 +10,10 @@ from pyparsing import ParseException
from osaca.parser import ParserAArch64, instructionForm
from osaca.parser.operand import Operand
from osaca.parser.directive import directiveOperand
from osaca.parser.memory import memoryOperand
from osaca.parser.register import registerOperand
from osaca.parser.immediate import immediateOperand
from osaca.parser.directive import DirectiveOperand
from osaca.parser.memory import MemoryOperand
from osaca.parser.register import RegisterOperand
from osaca.parser.immediate import ImmediateOperand
class TestParserAArch64(unittest.TestCase):
@@ -203,7 +203,7 @@ class TestParserAArch64(unittest.TestCase):
instruction_form_3 = instructionForm(
instruction_id=None,
operands_id=[],
directive_id=directiveOperand(name_id="cfi_def_cfa", parameter_id=["w29", "-16"]),
directive_id=DirectiveOperand(name_id="cfi_def_cfa", parameter_id=["w29", "-16"]),
comment_id=None,
label_id=None,
line=".cfi_def_cfa w29, -16",
@@ -212,10 +212,10 @@ class TestParserAArch64(unittest.TestCase):
instruction_form_4 = instructionForm(
instruction_id="ldr",
operands_id=[
registerOperand(prefix_id="s", name_id="0"),
memoryOperand(
RegisterOperand(prefix_id="s", name_id="0"),
MemoryOperand(
offset_ID=None,
base_id=registerOperand(prefix_id="x", name_id="11"),
base_id=RegisterOperand(prefix_id="x", name_id="11"),
index_id={
"prefix": "w",
"name": "10",
@@ -236,9 +236,9 @@ class TestParserAArch64(unittest.TestCase):
instruction_id="prfm",
operands_id=[
{"prfop": {"type": ["PLD"], "target": ["L1"], "policy": ["KEEP"]}},
memoryOperand(
MemoryOperand(
offset_ID={"value": 2048},
base_id=registerOperand(prefix_id="x", name_id="26"),
base_id=RegisterOperand(prefix_id="x", name_id="26"),
index_id=None,
scale_id=1,
),
@@ -252,11 +252,11 @@ class TestParserAArch64(unittest.TestCase):
instruction_form_6 = instructionForm(
instruction_id="stp",
operands_id=[
registerOperand(prefix_id="x", name_id="29"),
registerOperand(prefix_id="x", name_id="30"),
memoryOperand(
RegisterOperand(prefix_id="x", name_id="29"),
RegisterOperand(prefix_id="x", name_id="30"),
MemoryOperand(
offset_ID={"value": -16},
base_id=registerOperand(name_id="sp", prefix_id="x"),
base_id=RegisterOperand(name_id="sp", prefix_id="x"),
index_id=None,
scale_id=1,
pre_indexed=True,
@@ -271,11 +271,11 @@ class TestParserAArch64(unittest.TestCase):
instruction_form_7 = instructionForm(
instruction_id="ldp",
operands_id=[
registerOperand(prefix_id="q", name_id="2"),
registerOperand(prefix_id="q", name_id="3"),
memoryOperand(
RegisterOperand(prefix_id="q", name_id="2"),
RegisterOperand(prefix_id="q", name_id="3"),
MemoryOperand(
offset_ID=None,
base_id=registerOperand(name_id="11", prefix_id="x"),
base_id=RegisterOperand(name_id="11", prefix_id="x"),
index_id=None,
scale_id=1,
post_indexed={"value": 64},
@@ -290,11 +290,11 @@ class TestParserAArch64(unittest.TestCase):
instruction_form_8 = instructionForm(
instruction_id="fcmla",
operands_id=[
registerOperand(prefix_id="z", name_id="26", shape="d"),
registerOperand(prefix_id="p", name_id="0", predication="m"),
registerOperand(prefix_id="z", name_id="29", shape="d"),
registerOperand(prefix_id="z", name_id="21", shape="d"),
immediateOperand(value_id=90, type_id="int"),
RegisterOperand(prefix_id="z", name_id="26", shape="d"),
RegisterOperand(prefix_id="p", name_id="0", predication="m"),
RegisterOperand(prefix_id="z", name_id="29", shape="d"),
RegisterOperand(prefix_id="z", name_id="21", shape="d"),
ImmediateOperand(value_id=90, type_id="int"),
],
directive_id=None,
comment_id=None,
@@ -305,9 +305,9 @@ class TestParserAArch64(unittest.TestCase):
instruction_form_9 = instructionForm(
instruction_id="ccmn",
operands_id=[
registerOperand(prefix_id="x", name_id="11"),
immediateOperand(value_id=1, type_id="int"),
immediateOperand(value_id=3, type_id="int"),
RegisterOperand(prefix_id="x", name_id="11"),
ImmediateOperand(value_id=1, type_id="int"),
ImmediateOperand(value_id=3, type_id="int"),
{"condition": "EQ"},
],
directive_id=None,
@@ -372,17 +372,17 @@ class TestParserAArch64(unittest.TestCase):
instr_list_with_index = "ld4 {v0.S, v1.S, v2.S, v3.S}[2]"
instr_range_single = "dummy { z1.d }"
reg_list = [
registerOperand(prefix_id="x", name_id="5"),
registerOperand(prefix_id="x", name_id="6"),
registerOperand(prefix_id="x", name_id="7"),
RegisterOperand(prefix_id="x", name_id="5"),
RegisterOperand(prefix_id="x", name_id="6"),
RegisterOperand(prefix_id="x", name_id="7"),
]
reg_list_idx = [
registerOperand(prefix_id="v", name_id="0", shape="S", index=2),
registerOperand(prefix_id="v", name_id="1", shape="S", index=2),
registerOperand(prefix_id="v", name_id="2", shape="S", index=2),
registerOperand(prefix_id="v", name_id="3", shape="S", index=2),
RegisterOperand(prefix_id="v", name_id="0", shape="S", index=2),
RegisterOperand(prefix_id="v", name_id="1", shape="S", index=2),
RegisterOperand(prefix_id="v", name_id="2", shape="S", index=2),
RegisterOperand(prefix_id="v", name_id="3", shape="S", index=2),
]
reg_list_single = [registerOperand(prefix_id="z", name_id="1", shape="d")]
reg_list_single = [RegisterOperand(prefix_id="z", name_id="1", shape="d")]
prange = self.parser.parse_line(instr_range)
plist = self.parser.parse_line(instr_list)
@@ -397,22 +397,22 @@ class TestParserAArch64(unittest.TestCase):
# self.assertEqual(p_single.operands, reg_list_single)
def test_reg_dependency(self):
reg_1_1 = registerOperand(prefix_id="b", name_id="1")
reg_1_2 = registerOperand(prefix_id="h", name_id="1")
reg_1_3 = registerOperand(prefix_id="s", name_id="1")
reg_1_4 = registerOperand(prefix_id="d", name_id="1")
reg_1_4 = registerOperand(prefix_id="q", name_id="1")
reg_2_1 = registerOperand(prefix_id="w", name_id="2")
reg_2_2 = registerOperand(prefix_id="x", name_id="2")
reg_v1_1 = registerOperand(prefix_id="v", name_id="11", lanes="16", shape="b")
reg_v1_2 = registerOperand(prefix_id="v", name_id="11", lanes="8", shape="h")
reg_v1_3 = registerOperand(prefix_id="v", name_id="11", lanes="4", shape="s")
reg_v1_4 = registerOperand(prefix_id="v", name_id="11", lanes="2", shape="d")
reg_1_1 = RegisterOperand(prefix_id="b", name_id="1")
reg_1_2 = RegisterOperand(prefix_id="h", name_id="1")
reg_1_3 = RegisterOperand(prefix_id="s", name_id="1")
reg_1_4 = RegisterOperand(prefix_id="d", name_id="1")
reg_1_4 = RegisterOperand(prefix_id="q", name_id="1")
reg_2_1 = RegisterOperand(prefix_id="w", name_id="2")
reg_2_2 = RegisterOperand(prefix_id="x", name_id="2")
reg_v1_1 = RegisterOperand(prefix_id="v", name_id="11", lanes="16", shape="b")
reg_v1_2 = RegisterOperand(prefix_id="v", name_id="11", lanes="8", shape="h")
reg_v1_3 = RegisterOperand(prefix_id="v", name_id="11", lanes="4", shape="s")
reg_v1_4 = RegisterOperand(prefix_id="v", name_id="11", lanes="2", shape="d")
reg_b5 = registerOperand(prefix_id="b", name_id="5")
reg_q15 = registerOperand(prefix_id="q", name_id="15")
reg_v10 = registerOperand(prefix_id="v", name_id="10", lanes="2", shape="s")
reg_v20 = registerOperand(prefix_id="v", name_id="20", lanes="2", shape="d")
reg_b5 = RegisterOperand(prefix_id="b", name_id="5")
reg_q15 = RegisterOperand(prefix_id="q", name_id="15")
reg_v10 = RegisterOperand(prefix_id="v", name_id="10", lanes="2", shape="s")
reg_v20 = RegisterOperand(prefix_id="v", name_id="20", lanes="2", shape="d")
reg_1 = [reg_1_1, reg_1_2, reg_1_3, reg_1_4]
reg_2 = [reg_2_1, reg_2_2]

View File

@@ -9,7 +9,7 @@ import unittest
from pyparsing import ParseException
from osaca.parser import ParserX86ATT, instructionForm
from osaca.parser.register import registerOperand
from osaca.parser.register import RegisterOperand
class TestParserX86ATT(unittest.TestCase):
@@ -233,10 +233,10 @@ class TestParserX86ATT(unittest.TestCase):
register_str_3 = "%xmm1"
register_str_4 = "%rip"
parsed_reg_1 = registerOperand(name_id="rax")
parsed_reg_2 = registerOperand(name_id="r9")
parsed_reg_3 = registerOperand(name_id="xmm1")
parsed_reg_4 = registerOperand(name_id="rip")
parsed_reg_1 = RegisterOperand(name_id="rax")
parsed_reg_2 = RegisterOperand(name_id="r9")
parsed_reg_3 = RegisterOperand(name_id="xmm1")
parsed_reg_4 = RegisterOperand(name_id="rip")
self.assertEqual(self.parser.parse_register(register_str_1), parsed_reg_1)
self.assertEqual(self.parser.parse_register(register_str_2), parsed_reg_2)
@@ -259,22 +259,22 @@ class TestParserX86ATT(unittest.TestCase):
)
def test_reg_dependency(self):
reg_a1 = registerOperand(name_id="rax")
reg_a2 = registerOperand(name_id="eax")
reg_a3 = registerOperand(name_id="ax")
reg_a4 = registerOperand(name_id="al")
reg_r11 = registerOperand(name_id="r11")
reg_r11b = registerOperand(name_id="r11b")
reg_r11d = registerOperand(name_id="r11d")
reg_r11w = registerOperand(name_id="r11w")
reg_xmm1 = registerOperand(name_id="xmm1")
reg_ymm1 = registerOperand(name_id="ymm1")
reg_zmm1 = registerOperand(name_id="zmm1")
reg_a1 = RegisterOperand(name_id="rax")
reg_a2 = RegisterOperand(name_id="eax")
reg_a3 = RegisterOperand(name_id="ax")
reg_a4 = RegisterOperand(name_id="al")
reg_r11 = RegisterOperand(name_id="r11")
reg_r11b = RegisterOperand(name_id="r11b")
reg_r11d = RegisterOperand(name_id="r11d")
reg_r11w = RegisterOperand(name_id="r11w")
reg_xmm1 = RegisterOperand(name_id="xmm1")
reg_ymm1 = RegisterOperand(name_id="ymm1")
reg_zmm1 = RegisterOperand(name_id="zmm1")
reg_b1 = registerOperand(name_id="rbx")
reg_r15 = registerOperand(name_id="r15")
reg_xmm2 = registerOperand(name_id="xmm2")
reg_ymm3 = registerOperand(name_id="ymm3")
reg_b1 = RegisterOperand(name_id="rbx")
reg_r15 = RegisterOperand(name_id="r15")
reg_xmm2 = RegisterOperand(name_id="xmm2")
reg_ymm3 = RegisterOperand(name_id="ymm3")
reg_a = [reg_a1, reg_a2, reg_a3, reg_a4]
reg_r = [reg_r11, reg_r11b, reg_r11d, reg_r11w]

View File

@@ -19,9 +19,9 @@ from osaca.semantics import (
MachineModel,
reduce_to_section,
)
from osaca.parser.register import registerOperand
from osaca.parser.memory import memoryOperand
from osaca.parser.identifier import identifierOperand
from osaca.parser.register import RegisterOperand
from osaca.parser.memory import MemoryOperand
from osaca.parser.identifier import IdentifierOperand
class TestSemanticTools(unittest.TestCase):
@@ -94,6 +94,7 @@ class TestSemanticTools(unittest.TestCase):
)
cls.machine_model_zen = MachineModel(arch="zen1")
"""
for i in range(len(cls.kernel_x86)):
cls.semantics_csx.assign_src_dst(cls.kernel_x86[i])
cls.semantics_csx.assign_tp_lt(cls.kernel_x86[i])
@@ -103,9 +104,11 @@ class TestSemanticTools(unittest.TestCase):
for i in range(len(cls.kernel_x86_long_LCD)):
cls.semantics_csx.assign_src_dst(cls.kernel_x86_long_LCD[i])
cls.semantics_csx.assign_tp_lt(cls.kernel_x86_long_LCD[i])
for i in range(len(cls.kernel_AArch64)):
cls.semantics_tx2.assign_src_dst(cls.kernel_AArch64[i])
cls.semantics_tx2.assign_tp_lt(cls.kernel_AArch64[i])
for i in range(len(cls.kernel_aarch64_memdep)):
cls.semantics_tx2.assign_src_dst(cls.kernel_aarch64_memdep[i])
cls.semantics_tx2.assign_tp_lt(cls.kernel_aarch64_memdep[i])
@@ -115,7 +118,11 @@ class TestSemanticTools(unittest.TestCase):
for i in range(len(cls.kernel_aarch64_deps)):
cls.semantics_a64fx.assign_src_dst(cls.kernel_aarch64_deps[i])
cls.semantics_a64fx.assign_tp_lt(cls.kernel_aarch64_deps[i])
"""
print(cls.kernel_AArch64[2], "\n")
cls.semantics_tx2.assign_src_dst(cls.kernel_AArch64[2])
cls.semantics_tx2.assign_tp_lt(cls.kernel_AArch64[2])
print(cls.kernel_AArch64[2])
###########
# Tests
###########
@@ -126,7 +133,7 @@ class TestSemanticTools(unittest.TestCase):
ArchSemantics(tmp_mm)
except ValueError:
self.fail()
'''
def test_machine_model_various_functions(self):
# check dummy MachineModel creation
try:
@@ -148,31 +155,31 @@ class TestSemanticTools(unittest.TestCase):
self.assertIsNone(test_mm_arm.get_instruction("NOT_IN_DB", []))
name_x86_1 = "vaddpd"
operands_x86_1 = [
registerOperand(name_id="xmm"),
registerOperand(name_id="xmm"),
registerOperand(name_id="xmm"),
RegisterOperand(name_id="xmm"),
RegisterOperand(name_id="xmm"),
RegisterOperand(name_id="xmm"),
]
instr_form_x86_1 = test_mm_x86.get_instruction(name_x86_1, operands_x86_1)
self.assertEqual(instr_form_x86_1, test_mm_x86.get_instruction(name_x86_1, operands_x86_1))
self.assertEqual(
test_mm_x86.get_instruction("jg", [identifierOperand()]),
test_mm_x86.get_instruction("jg", [identifierOperand()]),
test_mm_x86.get_instruction("jg", [IdentifierOperand()]),
test_mm_x86.get_instruction("jg", [IdentifierOperand()]),
)
name_arm_1 = "fadd"
operands_arm_1 = [
registerOperand(prefix_id="v", shape="s"),
registerOperand(prefix_id="v", shape="s"),
registerOperand(prefix_id="v", shape="s"),
RegisterOperand(prefix_id="v", shape="s"),
RegisterOperand(prefix_id="v", shape="s"),
RegisterOperand(prefix_id="v", shape="s"),
]
instr_form_arm_1 = test_mm_arm.get_instruction(name_arm_1, operands_arm_1)
self.assertEqual(instr_form_arm_1, test_mm_arm.get_instruction(name_arm_1, operands_arm_1))
self.assertEqual(
test_mm_arm.get_instruction("b.ne", [identifierOperand()]),
test_mm_arm.get_instruction("b.ne", [identifierOperand()]),
test_mm_arm.get_instruction("b.ne", [IdentifierOperand()]),
test_mm_arm.get_instruction("b.ne", [IdentifierOperand()]),
)
self.assertEqual(
test_mm_arm.get_instruction("b.someNameThatDoesNotExist", [identifierOperand()]),
test_mm_arm.get_instruction("b.someOtherName", [identifierOperand()]),
test_mm_arm.get_instruction("b.someNameThatDoesNotExist", [IdentifierOperand()]),
test_mm_arm.get_instruction("b.someOtherName", [IdentifierOperand()]),
)
# test full instruction name
@@ -189,8 +196,8 @@ class TestSemanticTools(unittest.TestCase):
# test get_store_tp
self.assertEqual(
test_mm_x86.get_store_throughput(
memoryOperand(
base_id=registerOperand(name_id="x"), offset_ID=None, index_id=None, scale_id=1
MemoryOperand(
base_id=RegisterOperand(name_id="x"), offset_ID=None, index_id=None, scale_id=1
)
)[0].port_pressure,
[[2, "237"], [2, "4"]],
@@ -198,8 +205,8 @@ class TestSemanticTools(unittest.TestCase):
self.assertEqual(
test_mm_x86.get_store_throughput(
memoryOperand(
base_id=registerOperand(prefix_id="NOT_IN_DB"),
MemoryOperand(
base_id=RegisterOperand(prefix_id="NOT_IN_DB"),
offset_ID=None,
index_id="NOT_NONE",
scale_id=1,
@@ -210,8 +217,8 @@ class TestSemanticTools(unittest.TestCase):
self.assertEqual(
test_mm_arm.get_store_throughput(
memoryOperand(
base_id=registerOperand(prefix_id="x"),
MemoryOperand(
base_id=RegisterOperand(prefix_id="x"),
offset_ID=None,
index_id=None,
scale_id=1,
@@ -222,8 +229,8 @@ class TestSemanticTools(unittest.TestCase):
self.assertEqual(
test_mm_arm.get_store_throughput(
memoryOperand(
base_id=registerOperand(prefix_id="NOT_IN_DB"),
MemoryOperand(
base_id=RegisterOperand(prefix_id="NOT_IN_DB"),
offset_ID=None,
index_id=None,
scale_id=1,
@@ -234,16 +241,16 @@ class TestSemanticTools(unittest.TestCase):
# test get_store_lt
self.assertEqual(
test_mm_x86.get_store_latency(
memoryOperand(
base_id=registerOperand(name_id="x"), offset_ID=None, index_id=None, scale_id=1
MemoryOperand(
base_id=RegisterOperand(name_id="x"), offset_ID=None, index_id=None, scale_id=1
)
),
0,
)
self.assertEqual(
test_mm_arm.get_store_latency(
memoryOperand(
base_id=registerOperand(prefix_id="x"),
MemoryOperand(
base_id=RegisterOperand(prefix_id="x"),
offset_ID=None,
index_id=None,
scale_id=1,
@@ -258,8 +265,8 @@ class TestSemanticTools(unittest.TestCase):
# test default load tp
self.assertEqual(
test_mm_x86.get_load_throughput(
memoryOperand(
base_id=registerOperand(name_id="x"), offset_ID=None, index_id=None, scale_id=1
MemoryOperand(
base_id=RegisterOperand(name_id="x"), offset_ID=None, index_id=None, scale_id=1
)
)[0].port_pressure,
[[1, "23"], [1, ["2D", "3D"]]],
@@ -310,391 +317,397 @@ class TestSemanticTools(unittest.TestCase):
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
kernel_fixed = deepcopy(self.kernel_x86)
self.semantics_csx.add_semantics(kernel_fixed)
self.assertEqual(get_unmatched_instruction_ratio(kernel_fixed), 0)
"""
# x86
kernel_fixed = deepcopy(self.kernel_x86)
self.semantics_csx.add_semantics(kernel_fixed)
self.assertEqual(get_unmatched_instruction_ratio(kernel_fixed), 0)
kernel_optimal = deepcopy(kernel_fixed)
self.semantics_csx.assign_optimal_throughput(kernel_optimal)
tp_fixed = self.semantics_csx.get_throughput_sum(kernel_fixed)
tp_optimal = self.semantics_csx.get_throughput_sum(kernel_optimal)
self.assertNotEqual(tp_fixed, tp_optimal)
self.assertTrue(max(tp_optimal) <= max(tp_fixed))
# test multiple port assignment options
test_mm_x86 = MachineModel(path_to_yaml=self._find_file("test_db_x86.yml"))
tmp_semantics = ArchSemantics(test_mm_x86)
tmp_code_1 = "fantasyinstr1 %rax, %rax\n"
tmp_code_2 = "fantasyinstr1 %rax, %rax\nfantasyinstr2 %rbx, %rbx\n"
tmp_kernel_1 = self.parser_x86.parse_file(tmp_code_1)
tmp_kernel_2 = self.parser_x86.parse_file(tmp_code_2)
tmp_semantics.add_semantics(tmp_kernel_1)
tmp_semantics.add_semantics(tmp_kernel_2)
tmp_semantics.assign_optimal_throughput(tmp_kernel_1)
tmp_semantics.assign_optimal_throughput(tmp_kernel_2)
k1i1_pp = [round(x, 2) for x in tmp_kernel_1[0]["port_pressure"]]
k2i1_pp = [round(x, 2) for x in tmp_kernel_2[0]["port_pressure"]]
self.assertEqual(k1i1_pp, [0.33, 0.0, 0.33, 0.0, 0.0, 0.0, 0.0, 0.0, 0.33, 0.0, 0.0])
self.assertEqual(k2i1_pp, [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0])
kernel_optimal = deepcopy(kernel_fixed)
self.semantics_csx.assign_optimal_throughput(kernel_optimal)
tp_fixed = self.semantics_csx.get_throughput_sum(kernel_fixed)
tp_optimal = self.semantics_csx.get_throughput_sum(kernel_optimal)
self.assertNotEqual(tp_fixed, tp_optimal)
self.assertTrue(max(tp_optimal) <= max(tp_fixed))
# test multiple port assignment options
test_mm_x86 = MachineModel(path_to_yaml=self._find_file("test_db_x86.yml"))
tmp_semantics = ArchSemantics(test_mm_x86)
tmp_code_1 = "fantasyinstr1 %rax, %rax\n"
tmp_code_2 = "fantasyinstr1 %rax, %rax\nfantasyinstr2 %rbx, %rbx\n"
tmp_kernel_1 = self.parser_x86.parse_file(tmp_code_1)
tmp_kernel_2 = self.parser_x86.parse_file(tmp_code_2)
tmp_semantics.add_semantics(tmp_kernel_1)
tmp_semantics.add_semantics(tmp_kernel_2)
tmp_semantics.assign_optimal_throughput(tmp_kernel_1)
tmp_semantics.assign_optimal_throughput(tmp_kernel_2)
k1i1_pp = [round(x, 2) for x in tmp_kernel_1[0].port_pressure]
k2i1_pp = [round(x, 2) for x in tmp_kernel_2[0].port_pressure]
self.assertEqual(k1i1_pp, [0.33, 0.0, 0.33, 0.0, 0.0, 0.0, 0.0, 0.0, 0.33, 0.0, 0.0])
self.assertEqual(k2i1_pp, [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0])
# arm
kernel_fixed = deepcopy(self.kernel_AArch64)
self.semantics_tx2.add_semantics(kernel_fixed)
self.assertEqual(get_unmatched_instruction_ratio(kernel_fixed), 0)
kernel_optimal = deepcopy(kernel_fixed)
self.semantics_tx2.assign_optimal_throughput(kernel_optimal)
tp_fixed = self.semantics_tx2.get_throughput_sum(kernel_fixed)
tp_optimal = self.semantics_tx2.get_throughput_sum(kernel_optimal)
self.assertNotEqual(tp_fixed, tp_optimal)
self.assertTrue(max(tp_optimal) <= max(tp_fixed))
# arm
kernel_fixed = deepcopy(self.kernel_AArch64)
def test_kernelDG_x86(self):
#
# 4
# \___>6__>7
# /
# 3
# 5_______>9
#
dg = KernelDG(self.kernel_x86, self.parser_x86, self.machine_model_csx, self.semantics_csx)
self.assertTrue(nx.algorithms.dag.is_directed_acyclic_graph(dg.dg))
self.assertEqual(len(list(dg.get_dependent_instruction_forms(line_number=3))), 1)
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=3)), 6)
self.assertEqual(len(list(dg.get_dependent_instruction_forms(line_number=4))), 1)
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=4)), 6)
self.assertEqual(len(list(dg.get_dependent_instruction_forms(line_number=5))), 1)
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=5)), 9)
self.assertEqual(len(list(dg.get_dependent_instruction_forms(line_number=6))), 1)
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=6)), 7)
self.assertEqual(list(dg.get_dependent_instruction_forms(line_number=7)), [])
self.assertEqual(list(dg.get_dependent_instruction_forms(line_number=8)), [])
with self.assertRaises(ValueError):
dg.get_dependent_instruction_forms()
# test dot creation
dg.export_graph(filepath="/dev/null")
self.semantics_tx2.add_semantics(kernel_fixed)
def test_memdependency_x86(self):
dg = KernelDG(
self.kernel_x86_memdep,
self.parser_x86,
self.machine_model_csx,
self.semantics_csx,
)
self.assertTrue(nx.algorithms.dag.is_directed_acyclic_graph(dg.dg))
self.assertEqual(set(dg.get_dependent_instruction_forms(line_number=3)), {6, 8})
self.assertEqual(set(dg.get_dependent_instruction_forms(line_number=5)), {10, 12})
with self.assertRaises(ValueError):
dg.get_dependent_instruction_forms()
# test dot creation
dg.export_graph(filepath="/dev/null")
self.assertEqual(get_unmatched_instruction_ratio(kernel_fixed), 0)
def test_kernelDG_AArch64(self):
dg = KernelDG(
self.kernel_AArch64,
self.parser_AArch64,
self.machine_model_tx2,
self.semantics_tx2,
)
self.assertTrue(nx.algorithms.dag.is_directed_acyclic_graph(dg.dg))
self.assertEqual(set(dg.get_dependent_instruction_forms(line_number=3)), {7, 8})
self.assertEqual(set(dg.get_dependent_instruction_forms(line_number=4)), {9, 10})
self.assertEqual(set(dg.get_dependent_instruction_forms(line_number=5)), {6, 7, 8})
self.assertEqual(set(dg.get_dependent_instruction_forms(line_number=6)), {9, 10})
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=7)), 13)
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=8)), 14)
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=9)), 16)
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=10)), 17)
self.assertEqual(set(dg.get_dependent_instruction_forms(line_number=11)), {13, 14})
self.assertEqual(set(dg.get_dependent_instruction_forms(line_number=12)), {16, 17})
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=13)), 15)
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=14)), 15)
self.assertEqual(len(list(dg.get_dependent_instruction_forms(line_number=15))), 0)
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=16)), 18)
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=17)), 18)
self.assertEqual(len(list(dg.get_dependent_instruction_forms(line_number=18))), 0)
self.assertEqual(len(list(dg.get_dependent_instruction_forms(line_number=19))), 0)
self.assertEqual(len(list(dg.get_dependent_instruction_forms(line_number=20))), 0)
with self.assertRaises(ValueError):
dg.get_dependent_instruction_forms()
# test dot creation
dg.export_graph(filepath="/dev/null")
kernel_optimal = deepcopy(kernel_fixed)
self.semantics_tx2.assign_optimal_throughput(kernel_optimal)
tp_fixed = self.semantics_tx2.get_throughput_sum(kernel_fixed)
tp_optimal = self.semantics_tx2.get_throughput_sum(kernel_optimal)
self.assertNotEqual(tp_fixed, tp_optimal)
self.assertTrue(max(tp_optimal) <= max(tp_fixed))
def test_kernelDG_SVE(self):
KernelDG(
self.kernel_aarch64_SVE,
self.parser_AArch64,
self.machine_model_a64fx,
self.semantics_a64fx,
)
# TODO check for correct analysis
def test_kernelDG_x86(self):
#
# 4
# \___>6__>7
# /
# 3
# 5_______>9
#
dg = KernelDG(self.kernel_x86, self.parser_x86, self.machine_model_csx, self.semantics_csx)
self.assertTrue(nx.algorithms.dag.is_directed_acyclic_graph(dg.dg))
self.assertEqual(len(list(dg.get_dependent_instruction_forms(line_number=3))), 1)
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=3)), 6)
self.assertEqual(len(list(dg.get_dependent_instruction_forms(line_number=4))), 1)
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=4)), 6)
self.assertEqual(len(list(dg.get_dependent_instruction_forms(line_number=5))), 1)
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=5)), 9)
self.assertEqual(len(list(dg.get_dependent_instruction_forms(line_number=6))), 1)
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=6)), 7)
self.assertEqual(list(dg.get_dependent_instruction_forms(line_number=7)), [])
self.assertEqual(list(dg.get_dependent_instruction_forms(line_number=8)), [])
with self.assertRaises(ValueError):
dg.get_dependent_instruction_forms()
# test dot creation
dg.export_graph(filepath="/dev/null")
def test_hidden_load(self):
machine_model_hld = MachineModel(
path_to_yaml=self._find_file("hidden_load_machine_model.yml")
)
self.assertTrue(machine_model_hld.has_hidden_loads())
semantics_hld = ArchSemantics(machine_model_hld)
kernel_hld = self.parser_x86.parse_file(self.code_x86)
kernel_hld_2 = self.parser_x86.parse_file(self.code_x86)
kernel_hld_2 = self.parser_x86.parse_file(self.code_x86)[-3:]
kernel_hld_3 = self.parser_x86.parse_file(self.code_x86)[5:8]
semantics_hld.add_semantics(kernel_hld)
semantics_hld.add_semantics(kernel_hld_2)
semantics_hld.add_semantics(kernel_hld_3)
num_hidden_loads = len([x for x in kernel_hld if INSTR_flags.HIDDEN_LD in x.flags])
num_hidden_loads_2 = len([x for x in kernel_hld_2 if INSTR_flags.HIDDEN_LD in x.flags])
num_hidden_loads_3 = len([x for x in kernel_hld_3 if INSTR_flags.HIDDEN_LD in x.flags])
self.assertEqual(num_hidden_loads, 1)
self.assertEqual(num_hidden_loads_2, 0)
self.assertEqual(num_hidden_loads_3, 1)
def test_cyclic_dag(self):
dg = KernelDG(self.kernel_x86, self.parser_x86, self.machine_model_csx, self.semantics_csx)
dg.dg.add_edge(100, 101, latency=1.0)
dg.dg.add_edge(101, 102, latency=2.0)
dg.dg.add_edge(102, 100, latency=3.0)
with self.assertRaises(NotImplementedError):
dg.get_critical_path()
with self.assertRaises(NotImplementedError):
dg.get_loopcarried_dependencies()
def test_loop_carried_dependency_aarch64(self):
dg = KernelDG(
self.kernel_aarch64_memdep,
self.parser_AArch64,
self.machine_model_tx2,
self.semantics_tx2,
)
lc_deps = dg.get_loopcarried_dependencies()
self.assertEqual(len(lc_deps), 4)
# based on line 6
dep_path = "6-10-11-12-13-14"
self.assertEqual(lc_deps[dep_path]["latency"], 29.0)
self.assertEqual(
[(iform.line_number, lat) for iform, lat in lc_deps[dep_path]["dependencies"]],
[(6, 4.0), (10, 6.0), (11, 6.0), (12, 6.0), (13, 6.0), (14, 1.0)],
)
dg = KernelDG(
self.kernel_aarch64_deps,
self.parser_AArch64,
self.machine_model_a64fx,
self.semantics_a64fx,
flag_dependencies=True,
)
lc_deps = dg.get_loopcarried_dependencies()
self.assertEqual(len(lc_deps), 2)
# based on line 4
dep_path = "4-5-6-9-10-11-12"
self.assertEqual(lc_deps[dep_path]["latency"], 7.0)
self.assertEqual(
[(iform.line_number, lat) for iform, lat in lc_deps[dep_path]["dependencies"]],
[(4, 1.0), (5, 1.0), (6, 1.0), (9, 1.0), (10, 1.0), (11, 1.0), (12, 1.0)],
)
dg = KernelDG(
self.kernel_aarch64_deps,
self.parser_AArch64,
self.machine_model_a64fx,
self.semantics_a64fx,
flag_dependencies=False,
)
lc_deps = dg.get_loopcarried_dependencies()
self.assertEqual(len(lc_deps), 1)
# based on line 4
dep_path = "4-5-10-11-12"
self.assertEqual(lc_deps[dep_path]["latency"], 5.0)
self.assertEqual(
[(iform.line_number, lat) for iform, lat in lc_deps[dep_path]["dependencies"]],
[(4, 1.0), (5, 1.0), (10, 1.0), (11, 1.0), (12, 1.0)],
)
def test_loop_carried_dependency_x86(self):
lcd_id = "8"
lcd_id2 = "5"
dg = KernelDG(self.kernel_x86, self.parser_x86, self.machine_model_csx, self.semantics_csx)
lc_deps = dg.get_loopcarried_dependencies()
# self.assertEqual(len(lc_deps), 2)
# ID 8
self.assertEqual(
lc_deps[lcd_id]["root"], dg.dg.nodes(data=True)[int(lcd_id)]["instruction_form"]
)
self.assertEqual(len(lc_deps[lcd_id]["dependencies"]), 1)
self.assertEqual(
lc_deps[lcd_id]["dependencies"][0][0],
dg.dg.nodes(data=True)[int(lcd_id)]["instruction_form"],
)
# w/ flag dependencies: ID 9 w/ len=2
# w/o flag dependencies: ID 5 w/ len=1
# TODO discuss
self.assertEqual(
lc_deps[lcd_id2]["root"],
dg.dg.nodes(data=True)[int(lcd_id2)]["instruction_form"],
)
self.assertEqual(len(lc_deps[lcd_id2]["dependencies"]), 1)
self.assertEqual(
lc_deps[lcd_id2]["dependencies"][0][0],
dg.dg.nodes(data=True)[int(lcd_id2)]["instruction_form"],
)
def test_timeout_during_loop_carried_dependency(self):
start_time = time.perf_counter()
KernelDG(
self.kernel_x86_long_LCD,
self.parser_x86,
self.machine_model_csx,
self.semantics_x86,
timeout=10,
)
end_time = time.perf_counter()
time_10 = end_time - start_time
start_time = time.perf_counter()
KernelDG(
self.kernel_x86_long_LCD,
self.parser_x86,
self.machine_model_csx,
self.semantics_x86,
timeout=2,
)
end_time = time.perf_counter()
time_2 = end_time - start_time
# self.assertTrue(time_10 > 10)
self.assertTrue(2 < time_2)
# self.assertTrue(time_2 < (time_10 - 7))
def test_is_read_is_written_x86(self):
# independent form HW model
dag = KernelDG(self.kernel_x86, self.parser_x86, None, None)
reg_rcx = registerOperand(name_id="rcx")
reg_ymm1 = registerOperand(name_id="ymm1")
instr_form_r_c = self.parser_x86.parse_line("vmovsd %xmm0, (%r15,%rcx,8)")
self.semantics_csx.assign_src_dst(instr_form_r_c)
instr_form_non_r_c = self.parser_x86.parse_line("movl %xmm0, (%r15,%rax,8)")
self.semantics_csx.assign_src_dst(instr_form_non_r_c)
instr_form_w_c = self.parser_x86.parse_line("movi $0x05ACA, %rcx")
self.semantics_csx.assign_src_dst(instr_form_w_c)
instr_form_rw_ymm_1 = self.parser_x86.parse_line("vinsertf128 $0x1, %xmm1, %ymm0, %ymm1")
self.semantics_csx.assign_src_dst(instr_form_rw_ymm_1)
instr_form_rw_ymm_2 = self.parser_x86.parse_line("vinsertf128 $0x1, %xmm0, %ymm1, %ymm1")
self.semantics_csx.assign_src_dst(instr_form_rw_ymm_2)
instr_form_r_ymm = self.parser_x86.parse_line("vmovapd %ymm1, %ymm0")
self.semantics_csx.assign_src_dst(instr_form_r_ymm)
self.assertTrue(dag.is_read(reg_rcx, instr_form_r_c))
self.assertFalse(dag.is_read(reg_rcx, instr_form_non_r_c))
self.assertFalse(dag.is_read(reg_rcx, instr_form_w_c))
self.assertTrue(dag.is_written(reg_rcx, instr_form_w_c))
self.assertFalse(dag.is_written(reg_rcx, instr_form_r_c))
self.assertTrue(dag.is_read(reg_ymm1, instr_form_rw_ymm_1))
self.assertTrue(dag.is_read(reg_ymm1, instr_form_rw_ymm_2))
self.assertTrue(dag.is_read(reg_ymm1, instr_form_r_ymm))
self.assertTrue(dag.is_written(reg_ymm1, instr_form_rw_ymm_1))
self.assertTrue(dag.is_written(reg_ymm1, instr_form_rw_ymm_2))
self.assertFalse(dag.is_written(reg_ymm1, instr_form_r_ymm))
def test_is_read_is_written_AArch64(self):
# independent form HW model
dag = KernelDG(self.kernel_AArch64, self.parser_AArch64, None, None)
reg_x1 = registerOperand(prefix_id="x", name_id="1")
reg_w1 = registerOperand(prefix_id="w", name_id="1")
reg_d1 = registerOperand(prefix_id="d", name_id="1")
reg_q1 = registerOperand(prefix_id="q", name_id="1")
reg_v1 = registerOperand(prefix_id="v", name_id="1", lanes="2", shape="d")
regs = [reg_d1, reg_q1, reg_v1]
regs_gp = [reg_w1, reg_x1]
instr_form_r_1 = self.parser_AArch64.parse_line("stp q1, q3, [x12, #192]")
self.semantics_tx2.assign_src_dst(instr_form_r_1)
instr_form_r_2 = self.parser_AArch64.parse_line("fadd v2.2d, v1.2d, v0.2d")
self.semantics_tx2.assign_src_dst(instr_form_r_2)
instr_form_w_1 = self.parser_AArch64.parse_line("ldr d1, [x1, #:got_lo12:q2c]")
self.semantics_tx2.assign_src_dst(instr_form_w_1)
instr_form_non_w_1 = self.parser_AArch64.parse_line("ldr x1, [x1, #:got_lo12:q2c]")
self.semantics_tx2.assign_src_dst(instr_form_non_w_1)
instr_form_rw_1 = self.parser_AArch64.parse_line("fmul v1.2d, v1.2d, v0.2d")
self.semantics_tx2.assign_src_dst(instr_form_rw_1)
instr_form_rw_2 = self.parser_AArch64.parse_line("ldp q2, q4, [x1, #64]!")
self.semantics_tx2.assign_src_dst(instr_form_rw_2)
instr_form_rw_3 = self.parser_AArch64.parse_line("str x4, [x1], #64")
self.semantics_tx2.assign_src_dst(instr_form_rw_3)
instr_form_non_rw_1 = self.parser_AArch64.parse_line("adds x1, x11")
self.semantics_tx2.assign_src_dst(instr_form_non_rw_1)
for reg in regs:
with self.subTest(reg=reg):
# self.assertTrue(dag.is_read(reg, instr_form_r_1))
self.assertTrue(dag.is_read(reg, instr_form_r_2))
self.assertTrue(dag.is_read(reg, instr_form_rw_1))
self.assertFalse(dag.is_read(reg, instr_form_rw_2))
self.assertFalse(dag.is_read(reg, instr_form_rw_3))
self.assertFalse(dag.is_read(reg, instr_form_w_1))
self.assertTrue(dag.is_written(reg, instr_form_w_1))
self.assertTrue(dag.is_written(reg, instr_form_rw_1))
self.assertFalse(dag.is_written(reg, instr_form_non_w_1))
self.assertFalse(dag.is_written(reg, instr_form_rw_2))
self.assertFalse(dag.is_written(reg, instr_form_rw_3))
self.assertFalse(dag.is_written(reg, instr_form_non_rw_1))
self.assertFalse(dag.is_written(reg, instr_form_non_rw_1))
for reg in regs_gp:
with self.subTest(reg=reg):
self.assertFalse(dag.is_read(reg, instr_form_r_1))
self.assertFalse(dag.is_read(reg, instr_form_r_2))
self.assertFalse(dag.is_read(reg, instr_form_rw_1))
self.assertTrue(dag.is_read(reg, instr_form_rw_2))
self.assertTrue(dag.is_read(reg, instr_form_rw_3))
self.assertTrue(dag.is_read(reg, instr_form_w_1))
self.assertFalse(dag.is_written(reg, instr_form_w_1))
self.assertFalse(dag.is_written(reg, instr_form_rw_1))
self.assertTrue(dag.is_written(reg, instr_form_non_w_1))
self.assertTrue(dag.is_written(reg, instr_form_rw_2))
self.assertTrue(dag.is_written(reg, instr_form_rw_3))
self.assertTrue(dag.is_written(reg, instr_form_non_rw_1))
self.assertTrue(dag.is_written(reg, instr_form_non_rw_1))
def test_invalid_MachineModel(self):
with self.assertRaises(ValueError):
MachineModel()
with self.assertRaises(ValueError):
MachineModel(arch="CSX", path_to_yaml=os.path.join(self.MODULE_DATA_DIR, "csx.yml"))
with self.assertRaises(FileNotFoundError):
MachineModel(arch="THE_MACHINE")
with self.assertRaises(FileNotFoundError):
MachineModel(path_to_yaml=os.path.join(self.MODULE_DATA_DIR, "THE_MACHINE.yml"))
def test_MachineModel_getter(self):
sample_operands = [
memoryOperand(
offset_ID=None,
base_id=registerOperand(name_id="r12"),
index_id=registerOperand(name_id="rcx"),
scale_id=8,
def test_memdependency_x86(self):
dg = KernelDG(
self.kernel_x86_memdep,
self.parser_x86,
self.machine_model_csx,
self.semantics_csx,
)
]
self.assertIsNone(self.machine_model_csx.get_instruction("GETRESULT", sample_operands))
self.assertIsNone(self.machine_model_tx2.get_instruction("GETRESULT", sample_operands))
self.assertTrue(nx.algorithms.dag.is_directed_acyclic_graph(dg.dg))
self.assertEqual(set(dg.get_dependent_instruction_forms(line_number=3)), {6, 8})
self.assertEqual(set(dg.get_dependent_instruction_forms(line_number=5)), {10, 12})
with self.assertRaises(ValueError):
dg.get_dependent_instruction_forms()
# test dot creation
dg.export_graph(filepath="/dev/null")
self.assertEqual(self.machine_model_csx.get_arch(), "csx")
self.assertEqual(self.machine_model_tx2.get_arch(), "tx2")
def test_kernelDG_AArch64(self):
dg = KernelDG(
self.kernel_AArch64,
self.parser_AArch64,
self.machine_model_tx2,
self.semantics_tx2,
)
self.assertTrue(nx.algorithms.dag.is_directed_acyclic_graph(dg.dg))
self.assertEqual(set(dg.get_dependent_instruction_forms(line_number=3)), {7, 8})
self.assertEqual(set(dg.get_dependent_instruction_forms(line_number=4)), {9, 10})
self.assertEqual(set(dg.get_dependent_instruction_forms(line_number=5)), {6, 7, 8})
self.assertEqual(set(dg.get_dependent_instruction_forms(line_number=6)), {9, 10})
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=7)), 13)
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=8)), 14)
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=9)), 16)
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=10)), 17)
self.assertEqual(set(dg.get_dependent_instruction_forms(line_number=11)), {13, 14})
self.assertEqual(set(dg.get_dependent_instruction_forms(line_number=12)), {16, 17})
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=13)), 15)
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=14)), 15)
self.assertEqual(len(list(dg.get_dependent_instruction_forms(line_number=15))), 0)
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=16)), 18)
self.assertEqual(next(dg.get_dependent_instruction_forms(line_number=17)), 18)
self.assertEqual(len(list(dg.get_dependent_instruction_forms(line_number=18))), 0)
self.assertEqual(len(list(dg.get_dependent_instruction_forms(line_number=19))), 0)
self.assertEqual(len(list(dg.get_dependent_instruction_forms(line_number=20))), 0)
with self.assertRaises(ValueError):
dg.get_dependent_instruction_forms()
# test dot creation
dg.export_graph(filepath="/dev/null")
self.assertEqual(self.machine_model_csx.get_ISA(), "x86")
self.assertEqual(self.machine_model_tx2.get_ISA(), "aarch64")
def test_kernelDG_SVE(self):
KernelDG(
self.kernel_aarch64_SVE,
self.parser_AArch64,
self.machine_model_a64fx,
self.semantics_a64fx,
)
# TODO check for correct analysis
ports_csx = ["0", "0DV", "1", "2", "2D", "3", "3D", "4", "5", "6", "7"]
data_ports_csx = ["2D", "3D"]
self.assertEqual(self.machine_model_csx.get_ports(), ports_csx)
self.assertEqual(self.machine_model_csx.get_data_ports(), data_ports_csx)
def test_hidden_load(self):
machine_model_hld = MachineModel(
path_to_yaml=self._find_file("hidden_load_machine_model.yml")
)
self.assertTrue(machine_model_hld.has_hidden_loads())
semantics_hld = ArchSemantics(machine_model_hld)
kernel_hld = self.parser_x86.parse_file(self.code_x86)
kernel_hld_2 = self.parser_x86.parse_file(self.code_x86)
kernel_hld_2 = self.parser_x86.parse_file(self.code_x86)[-3:]
kernel_hld_3 = self.parser_x86.parse_file(self.code_x86)[5:8]
self.assertFalse(self.machine_model_tx2.has_hidden_loads())
semantics_hld.add_semantics(kernel_hld)
semantics_hld.add_semantics(kernel_hld_2)
semantics_hld.add_semantics(kernel_hld_3)
self.assertEqual(MachineModel.get_isa_for_arch("CSX"), "x86")
self.assertEqual(MachineModel.get_isa_for_arch("tX2"), "aarch64")
with self.assertRaises(ValueError):
self.assertIsNone(MachineModel.get_isa_for_arch("THE_MACHINE"))
num_hidden_loads = len([x for x in kernel_hld if INSTR_flags.HIDDEN_LD in x.flags])
num_hidden_loads_2 = len([x for x in kernel_hld_2 if INSTR_flags.HIDDEN_LD in x.flags])
num_hidden_loads_3 = len([x for x in kernel_hld_3 if INSTR_flags.HIDDEN_LD in x.flags])
self.assertEqual(num_hidden_loads, 1)
self.assertEqual(num_hidden_loads_2, 0)
self.assertEqual(num_hidden_loads_3, 1)
def test_cyclic_dag(self):
dg = KernelDG(self.kernel_x86, self.parser_x86, self.machine_model_csx, self.semantics_csx)
dg.dg.add_edge(100, 101, latency=1.0)
dg.dg.add_edge(101, 102, latency=2.0)
dg.dg.add_edge(102, 100, latency=3.0)
with self.assertRaises(NotImplementedError):
dg.get_critical_path()
with self.assertRaises(NotImplementedError):
dg.get_loopcarried_dependencies()
def test_loop_carried_dependency_aarch64(self):
dg = KernelDG(
self.kernel_aarch64_memdep,
self.parser_AArch64,
self.machine_model_tx2,
self.semantics_tx2,
)
lc_deps = dg.get_loopcarried_dependencies()
self.assertEqual(len(lc_deps), 4)
# based on line 6
dep_path = "6-10-11-12-13-14"
self.assertEqual(lc_deps[dep_path]["latency"], 29.0)
self.assertEqual(
[(iform.line_number, lat) for iform, lat in lc_deps[dep_path]["dependencies"]],
[(6, 4.0), (10, 6.0), (11, 6.0), (12, 6.0), (13, 6.0), (14, 1.0)],
)
dg = KernelDG(
self.kernel_aarch64_deps,
self.parser_AArch64,
self.machine_model_a64fx,
self.semantics_a64fx,
flag_dependencies=True,
)
lc_deps = dg.get_loopcarried_dependencies()
self.assertEqual(len(lc_deps), 2)
# based on line 4
dep_path = "4-5-6-9-10-11-12"
self.assertEqual(lc_deps[dep_path]["latency"], 7.0)
self.assertEqual(
[(iform.line_number, lat) for iform, lat in lc_deps[dep_path]["dependencies"]],
[(4, 1.0), (5, 1.0), (6, 1.0), (9, 1.0), (10, 1.0), (11, 1.0), (12, 1.0)],
)
dg = KernelDG(
self.kernel_aarch64_deps,
self.parser_AArch64,
self.machine_model_a64fx,
self.semantics_a64fx,
flag_dependencies=False,
)
lc_deps = dg.get_loopcarried_dependencies()
self.assertEqual(len(lc_deps), 1)
# based on line 4
dep_path = "4-5-10-11-12"
self.assertEqual(lc_deps[dep_path]["latency"], 5.0)
self.assertEqual(
[(iform.line_number, lat) for iform, lat in lc_deps[dep_path]["dependencies"]],
[(4, 1.0), (5, 1.0), (10, 1.0), (11, 1.0), (12, 1.0)],
)
def test_loop_carried_dependency_x86(self):
lcd_id = "8"
lcd_id2 = "5"
dg = KernelDG(self.kernel_x86, self.parser_x86, self.machine_model_csx, self.semantics_csx)
lc_deps = dg.get_loopcarried_dependencies()
# self.assertEqual(len(lc_deps), 2)
# ID 8
self.assertEqual(
lc_deps[lcd_id]["root"], dg.dg.nodes(data=True)[int(lcd_id)]["instruction_form"]
)
self.assertEqual(len(lc_deps[lcd_id]["dependencies"]), 1)
self.assertEqual(
lc_deps[lcd_id]["dependencies"][0][0],
dg.dg.nodes(data=True)[int(lcd_id)]["instruction_form"],
)
# w/ flag dependencies: ID 9 w/ len=2
# w/o flag dependencies: ID 5 w/ len=1
# TODO discuss
self.assertEqual(
lc_deps[lcd_id2]["root"],
dg.dg.nodes(data=True)[int(lcd_id2)]["instruction_form"],
)
self.assertEqual(len(lc_deps[lcd_id2]["dependencies"]), 1)
self.assertEqual(
lc_deps[lcd_id2]["dependencies"][0][0],
dg.dg.nodes(data=True)[int(lcd_id2)]["instruction_form"],
)
def test_timeout_during_loop_carried_dependency(self):
start_time = time.perf_counter()
KernelDG(
self.kernel_x86_long_LCD,
self.parser_x86,
self.machine_model_csx,
self.semantics_x86,
timeout=10,
)
end_time = time.perf_counter()
time_10 = end_time - start_time
start_time = time.perf_counter()
KernelDG(
self.kernel_x86_long_LCD,
self.parser_x86,
self.machine_model_csx,
self.semantics_x86,
timeout=2,
)
end_time = time.perf_counter()
time_2 = end_time - start_time
# self.assertTrue(time_10 > 10)
self.assertTrue(2 < time_2)
# self.assertTrue(time_2 < (time_10 - 7))
def test_is_read_is_written_x86(self):
# independent form HW model
dag = KernelDG(self.kernel_x86, self.parser_x86, None, None)
reg_rcx = RegisterOperand(name_id="rcx")
reg_ymm1 = RegisterOperand(name_id="ymm1")
instr_form_r_c = self.parser_x86.parse_line("vmovsd %xmm0, (%r15,%rcx,8)")
self.semantics_csx.assign_src_dst(instr_form_r_c)
instr_form_non_r_c = self.parser_x86.parse_line("movl %xmm0, (%r15,%rax,8)")
self.semantics_csx.assign_src_dst(instr_form_non_r_c)
instr_form_w_c = self.parser_x86.parse_line("movi $0x05ACA, %rcx")
self.semantics_csx.assign_src_dst(instr_form_w_c)
instr_form_rw_ymm_1 = self.parser_x86.parse_line("vinsertf128 $0x1, %xmm1, %ymm0, %ymm1")
self.semantics_csx.assign_src_dst(instr_form_rw_ymm_1)
instr_form_rw_ymm_2 = self.parser_x86.parse_line("vinsertf128 $0x1, %xmm0, %ymm1, %ymm1")
self.semantics_csx.assign_src_dst(instr_form_rw_ymm_2)
instr_form_r_ymm = self.parser_x86.parse_line("vmovapd %ymm1, %ymm0")
self.semantics_csx.assign_src_dst(instr_form_r_ymm)
self.assertTrue(dag.is_read(reg_rcx, instr_form_r_c))
self.assertFalse(dag.is_read(reg_rcx, instr_form_non_r_c))
self.assertFalse(dag.is_read(reg_rcx, instr_form_w_c))
self.assertTrue(dag.is_written(reg_rcx, instr_form_w_c))
self.assertFalse(dag.is_written(reg_rcx, instr_form_r_c))
self.assertTrue(dag.is_read(reg_ymm1, instr_form_rw_ymm_1))
self.assertTrue(dag.is_read(reg_ymm1, instr_form_rw_ymm_2))
self.assertTrue(dag.is_read(reg_ymm1, instr_form_r_ymm))
self.assertTrue(dag.is_written(reg_ymm1, instr_form_rw_ymm_1))
self.assertTrue(dag.is_written(reg_ymm1, instr_form_rw_ymm_2))
self.assertFalse(dag.is_written(reg_ymm1, instr_form_r_ymm))
def test_is_read_is_written_AArch64(self):
# independent form HW model
dag = KernelDG(self.kernel_AArch64, self.parser_AArch64, None, None)
reg_x1 = RegisterOperand(prefix_id="x", name_id="1")
reg_w1 = RegisterOperand(prefix_id="w", name_id="1")
reg_d1 = RegisterOperand(prefix_id="d", name_id="1")
reg_q1 = RegisterOperand(prefix_id="q", name_id="1")
reg_v1 = RegisterOperand(prefix_id="v", name_id="1", lanes="2", shape="d")
regs = [reg_d1, reg_q1, reg_v1]
regs_gp = [reg_w1, reg_x1]
instr_form_r_1 = self.parser_AArch64.parse_line("stp q1, q3, [x12, #192]")
self.semantics_tx2.assign_src_dst(instr_form_r_1)
instr_form_r_2 = self.parser_AArch64.parse_line("fadd v2.2d, v1.2d, v0.2d")
self.semantics_tx2.assign_src_dst(instr_form_r_2)
instr_form_w_1 = self.parser_AArch64.parse_line("ldr d1, [x1, #:got_lo12:q2c]")
self.semantics_tx2.assign_src_dst(instr_form_w_1)
instr_form_non_w_1 = self.parser_AArch64.parse_line("ldr x1, [x1, #:got_lo12:q2c]")
self.semantics_tx2.assign_src_dst(instr_form_non_w_1)
instr_form_rw_1 = self.parser_AArch64.parse_line("fmul v1.2d, v1.2d, v0.2d")
self.semantics_tx2.assign_src_dst(instr_form_rw_1)
instr_form_rw_2 = self.parser_AArch64.parse_line("ldp q2, q4, [x1, #64]!")
self.semantics_tx2.assign_src_dst(instr_form_rw_2)
instr_form_rw_3 = self.parser_AArch64.parse_line("str x4, [x1], #64")
self.semantics_tx2.assign_src_dst(instr_form_rw_3)
instr_form_non_rw_1 = self.parser_AArch64.parse_line("adds x1, x11")
self.semantics_tx2.assign_src_dst(instr_form_non_rw_1)
for reg in regs:
with self.subTest(reg=reg):
self.assertTrue(dag.is_read(reg, instr_form_r_1))
self.assertTrue(dag.is_read(reg, instr_form_r_2))
self.assertTrue(dag.is_read(reg, instr_form_rw_1))
self.assertFalse(dag.is_read(reg, instr_form_rw_2))
self.assertFalse(dag.is_read(reg, instr_form_rw_3))
self.assertFalse(dag.is_read(reg, instr_form_w_1))
self.assertTrue(dag.is_written(reg, instr_form_w_1))
self.assertTrue(dag.is_written(reg, instr_form_rw_1))
self.assertFalse(dag.is_written(reg, instr_form_non_w_1))
self.assertFalse(dag.is_written(reg, instr_form_rw_2))
self.assertFalse(dag.is_written(reg, instr_form_rw_3))
self.assertFalse(dag.is_written(reg, instr_form_non_rw_1))
self.assertFalse(dag.is_written(reg, instr_form_non_rw_1))
for reg in regs_gp:
with self.subTest(reg=reg):
self.assertFalse(dag.is_read(reg, instr_form_r_1))
self.assertFalse(dag.is_read(reg, instr_form_r_2))
self.assertFalse(dag.is_read(reg, instr_form_rw_1))
self.assertTrue(dag.is_read(reg, instr_form_rw_2))
self.assertTrue(dag.is_read(reg, instr_form_rw_3))
self.assertTrue(dag.is_read(reg, instr_form_w_1))
self.assertFalse(dag.is_written(reg, instr_form_w_1))
self.assertFalse(dag.is_written(reg, instr_form_rw_1))
self.assertTrue(dag.is_written(reg, instr_form_non_w_1))
self.assertTrue(dag.is_written(reg, instr_form_rw_2))
self.assertTrue(dag.is_written(reg, instr_form_rw_3))
self.assertTrue(dag.is_written(reg, instr_form_non_rw_1))
self.assertTrue(dag.is_written(reg, instr_form_non_rw_1))
def test_invalid_MachineModel(self):
with self.assertRaises(ValueError):
MachineModel()
with self.assertRaises(ValueError):
MachineModel(arch="CSX", path_to_yaml=os.path.join(self.MODULE_DATA_DIR, "csx.yml"))
with self.assertRaises(FileNotFoundError):
MachineModel(arch="THE_MACHINE")
with self.assertRaises(FileNotFoundError):
MachineModel(path_to_yaml=os.path.join(self.MODULE_DATA_DIR, "THE_MACHINE.yml"))
def test_MachineModel_getter(self):
sample_operands = [
MemoryOperand(
offset_ID=None,
base_id=RegisterOperand(name_id="r12"),
index_id=RegisterOperand(name_id="rcx"),
scale_id=8,
)
]
self.assertIsNone(self.machine_model_csx.get_instruction("GETRESULT", sample_operands))
self.assertIsNone(self.machine_model_tx2.get_instruction("GETRESULT", sample_operands))
self.assertEqual(self.machine_model_csx.get_arch(), "csx")
self.assertEqual(self.machine_model_tx2.get_arch(), "tx2")
self.assertEqual(self.machine_model_csx.get_ISA(), "x86")
self.assertEqual(self.machine_model_tx2.get_ISA(), "aarch64")
ports_csx = ["0", "0DV", "1", "2", "2D", "3", "3D", "4", "5", "6", "7"]
data_ports_csx = ["2D", "3D"]
self.assertEqual(self.machine_model_csx.get_ports(), ports_csx)
self.assertEqual(self.machine_model_csx.get_data_ports(), data_ports_csx)
self.assertFalse(self.machine_model_tx2.has_hidden_loads())
self.assertEqual(MachineModel.get_isa_for_arch("CSX"), "x86")
self.assertEqual(MachineModel.get_isa_for_arch("tX2"), "aarch64")
with self.assertRaises(ValueError):
self.assertIsNone(MachineModel.get_isa_for_arch("THE_MACHINE"))
"""
##################
# Helper functions