Merge branch 'master' into feat/spr

This commit is contained in:
JanLJL
2024-05-02 21:19:10 +02:00
50 changed files with 3969 additions and 2856 deletions

603
osaca/semantics/hw_model.py Executable file → Normal file
View File

@@ -6,13 +6,21 @@ import pickle
import re
import string
from collections import defaultdict
from copy import deepcopy
from itertools import product
from pathlib import Path
import ruamel.yaml
from osaca import __version__, utils
from osaca.parser import ParserX86ATT
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.condition import ConditionOperand
from osaca.parser.flag import FlagOperand
from osaca.parser.prefetch import PrefetchOperand
from ruamel.yaml.compat import StringIO
@@ -41,7 +49,6 @@ class MachineModel(object):
"index": i,
"offset": o,
"scale": s,
"port_pressure": [],
}
for b, i, o, s in product(["gpr"], ["gpr", None], ["imd", None], [1, 8])
],
@@ -96,11 +103,88 @@ class MachineModel(object):
self._data["instruction_forms"].remove(entry)
# Normalize instruction_form names (to UPPERCASE) and build dict for faster access:
self._data["instruction_forms_dict"] = defaultdict(list)
for iform in self._data["instruction_forms"]:
iform["name"] = iform["name"].upper()
self._data["instruction_forms_dict"][iform["name"]].append(iform)
if iform["operands"] != []:
new_operands = []
# Change operand types from dicts to classes
for o in iform["operands"]:
self.operand_to_class(o, new_operands)
iform["operands"] = new_operands
# Do the same for hidden operands
if "hidden_operands" in iform:
new_operands = []
# Change operand types from dicts to classes
for o in iform["hidden_operands"]:
self.operand_to_class(o, new_operands)
iform["hidden_operands"] = new_operands
# Change dict iform style to class style
new_iform = InstructionForm(
mnemonic=iform["name"].upper() if "name" in iform else None,
operands=iform["operands"] if "operands" in iform else [],
hidden_operands=(
iform["hidden_operands"] if "hidden_operands" in iform else []
),
directive_id=iform["directive"] if "directive" in iform else None,
comment_id=iform["comment"] if "comment" in iform else None,
line=iform["line"] if "line" in iform else None,
line_number=iform["line_number"] if "line_number" in iform else None,
latency=iform["latency"] if "latency" in iform else None,
throughput=iform["throughput"] if "throughput" in iform else None,
uops=iform["uops"] if "uops" in iform else None,
port_pressure=iform["port_pressure"] if "port_pressure" in iform else None,
operation=iform["operation"] if "operation" in iform else None,
breaks_dependency_on_equal_operands=(
iform["breaks_dependency_on_equal_operands"]
if "breaks_dependency_on_equal_operands" in iform
else False
),
semantic_operands=(
iform["semantic_operands"]
if "semantic_operands" in iform
else {"source": [], "destination": [], "src_dst": []}
),
)
# List containing classes with same name/instruction
self._data["instruction_forms_dict"][iform["name"]].append(new_iform)
self._data["internal_version"] = self.INTERNAL_VERSION
# Convert load and store throughput memory operands to classes
new_throughputs = []
if "load_throughput" in self._data:
for m in self._data["load_throughput"]:
new_throughputs.append(
(
MemoryOperand(
base=m["base"],
offset=m["offset"],
scale=m["scale"],
index=m["index"],
dst=m["dst"] if "dst" in m else None,
),
m["port_pressure"],
)
)
self._data["load_throughput"] = new_throughputs
new_throughputs = []
if "store_throughput" in self._data:
for m in self._data["store_throughput"]:
new_throughputs.append(
(
MemoryOperand(
base=m["base"],
offset=m["offset"],
scale=m["scale"],
index=m["index"],
),
m["port_pressure"],
)
)
self._data["store_throughput"] = new_throughputs
if not lazy:
# cache internal representation for future use
self._write_in_cache(self._path)
@@ -108,6 +192,84 @@ class MachineModel(object):
if not lazy:
MachineModel._runtime_cache[self._path] = self._data
def operand_to_class(self, o, new_operands):
"""Convert an operand from dict type to class"""
if o["class"] == "register":
new_operands.append(
RegisterOperand(
name=o["name"] if "name" in o else None,
prefix=o["prefix"] if "prefix" in o else None,
shape=o["shape"] if "shape" in o else None,
mask=o["mask"] if "mask" in o else False,
pre_indexed=o["pre_indexed"] if "pre_indexed" in o else False,
post_indexed=o["post_indexed"] if "post_indexed" in o else False,
source=o["source"] if "source" in o else False,
destination=o["destination"] if "destination" in o else False,
)
)
elif o["class"] == "memory":
if isinstance(o["base"], dict):
o["base"] = RegisterOperand(name=o["base"]["name"])
if isinstance(o["index"], dict):
o["index"] = RegisterOperand(
name=o["index"]["name"],
prefix=o["index"]["prefix"] if "prefix" in o["index"] else None,
)
new_operands.append(
MemoryOperand(
base=o["base"],
offset=o["offset"],
index=o["index"],
scale=o["scale"],
source=o["source"] if "source" in o else False,
destination=o["destination"] if "destination" in o else False,
pre_indexed=o["pre_indexed"] if "pre_indexed" in o else False,
post_indexed=o["post_indexed"] if "post_indexed" in o else False,
)
)
elif o["class"] == "immediate":
new_operands.append(
ImmediateOperand(
imd_type=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(
name=o["name"] if "name" in o else None,
source=o["source"] if "source" in o else False,
destination=o["destination"] if "destination" in o else False,
)
)
elif o["class"] == "condition":
new_operands.append(
ConditionOperand(
ccode=o["ccode"].upper(),
source=o["source"] if "source" in o else False,
destination=o["destination"] if "destination" in o else False,
)
)
elif o["class"] == "flag":
new_operands.append(
FlagOperand(
name=o["name"],
source=o["source"] if "source" in o else False,
destination=o["destination"] if "destination" in o else False,
)
)
elif o["class"] == "prfop":
new_operands.append(
PrefetchOperand(
type_id=o["type"] if "type" in o else None,
target=o["target"] if "target" in o else None,
policy=o["policy"] if "policy" in o else None,
)
)
else:
new_operands.append(o)
def get(self, key, default=None):
"""Return config entry for key or default/None."""
return self._data.get(key, default)
@@ -128,12 +290,13 @@ class MachineModel(object):
if name is None:
return None
name_matched_iforms = self._data["instruction_forms_dict"].get(name.upper(), [])
try:
return next(
instruction_form
for instruction_form in name_matched_iforms
if self._match_operands(
instruction_form["operands"] if "operands" in instruction_form else [],
instruction_form.operands,
operands,
)
)
@@ -162,7 +325,7 @@ class MachineModel(object):
def set_instruction(
self,
name,
mnemonic,
operands=None,
latency=None,
port_pressure=None,
@@ -171,28 +334,30 @@ class MachineModel(object):
):
"""Import instruction form information."""
# If it already exists. Overwrite information.
instr_data = self.get_instruction(name, operands)
instr_data = self.get_instruction(mnemonic, operands)
if instr_data is None:
instr_data = {}
instr_data = InstructionForm()
self._data["instruction_forms"].append(instr_data)
self._data["instruction_forms_dict"][name].append(instr_data)
self._data["instruction_forms_dict"][mnemonic].append(instr_data)
instr_data["name"] = name
instr_data["operands"] = operands
instr_data["latency"] = latency
instr_data["port_pressure"] = port_pressure
instr_data["throughput"] = throughput
instr_data["uops"] = uops
instr_data.mnemonic = mnemonic
instr_data.operands = operands
instr_data.latency = latency
instr_data.port_pressure = port_pressure
instr_data.throughput = throughput
instr_data.uops = uops
def set_instruction_entry(self, entry):
"""Import instruction as entry object form information."""
if entry.mnemonic is None and entry.operands == []:
raise KeyError
self.set_instruction(
entry["name"],
entry["operands"] if "operands" in entry else None,
entry["latency"] if "latency" in entry else None,
entry["port_pressure"] if "port_pressure" in entry else None,
entry["throughput"] if "throughput" in entry else None,
entry["uops"] if "uops" in entry else None,
entry.mnemonic,
entry.operands,
entry.latency,
entry.port_pressure,
entry.throughput,
entry.uops,
)
def add_port(self, port):
@@ -224,10 +389,10 @@ class MachineModel(object):
def get_load_throughput(self, memory):
"""Return load thorughput for given register type."""
ld_tp = [m for m in self._data["load_throughput"] if self._match_mem_entries(memory, m)]
ld_tp = [m for m in self._data["load_throughput"] if self._match_mem_entries(memory, m[0])]
if len(ld_tp) > 0:
return ld_tp.copy()
return [{"port_pressure": self._data["load_throughput_default"].copy()}]
return [(memory, self._data["load_throughput_default"].copy())]
def get_store_latency(self, reg_type):
"""Return store latency for given register type."""
@@ -236,16 +401,19 @@ class MachineModel(object):
def get_store_throughput(self, memory, src_reg=None):
"""Return store throughput for a given destination and register type."""
st_tp = [m for m in self._data["store_throughput"] if self._match_mem_entries(memory, m)]
st_tp = [
m for m in self._data["store_throughput"] if self._match_mem_entries(memory, m[0])
]
if src_reg is not None:
st_tp = [
tp
for tp in st_tp
if "src" in tp and self._check_operands(src_reg, {"register": {"name": tp["src"]}})
if "src" in tp[0]
and self._check_operands(src_reg, RegisterOperand(name=tp[0]["src"]))
]
if len(st_tp) > 0:
return st_tp.copy()
return [{"port_pressure": self._data["store_throughput_default"].copy()}]
return [(memory, 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."""
@@ -260,18 +428,6 @@ class MachineModel(object):
data_ports = [x for x in filter(data_port.match, self._data["ports"])]
return data_ports
@staticmethod
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"]:
op_attrs = [
y + ":" + str(op[y])
for y in list(filter(lambda x: True if x != "class" else False, op))
]
operands.append("{}({})".format(op["class"], ",".join(op_attrs)))
return "{} {}".format(instruction_form["name"].lower(), ",".join(operands))
@staticmethod
def get_isa_for_arch(arch):
"""Return ISA for given micro-arch ``arch``."""
@@ -311,23 +467,75 @@ class MachineModel(object):
else:
raise ValueError("Unknown architecture {!r}.".format(arch))
def class_to_dict(self, op):
"""Need to convert operand classes to dicts for the dump. Memory operand types may have their index/base/offset as a register operand/"""
if isinstance(op, Operand):
dict_op = dict(
(key.lstrip("_"), value)
for key, value in op.__dict__.items()
if not callable(value) and not key.startswith("__")
)
if isinstance(op, MemoryOperand):
if isinstance(dict_op["index"], Operand):
dict_op["index"] = dict(
(key.lstrip("_"), value)
for key, value in dict_op["index"].__dict__.items()
if not callable(value) and not key.startswith("__")
)
if isinstance(dict_op["offset"], Operand):
dict_op["offset"] = dict(
(key.lstrip("_"), value)
for key, value in dict_op["offset"].__dict__.items()
if not callable(value) and not key.startswith("__")
)
if isinstance(dict_op["base"], Operand):
dict_op["base"] = dict(
(key.lstrip("_"), value)
for key, value in dict_op["base"].__dict__.items()
if not callable(value) and not key.startswith("__")
)
return dict_op
return op
def dump(self, stream=None):
"""Dump machine model to stream or return it as a ``str`` if no stream is given."""
# Replace instruction form's port_pressure with styled version for RoundtripDumper
formatted_instruction_forms = deepcopy(self._data["instruction_forms"])
for instruction_form in formatted_instruction_forms:
formatted_instruction_forms = []
for instruction_form in self._data["instruction_forms"]:
if isinstance(instruction_form, InstructionForm):
instruction_form = dict(
(key.lstrip("_"), value)
for key, value in instruction_form.__dict__.items()
if not callable(value) and not key.startswith("__")
)
if instruction_form["port_pressure"] is not None:
cs = ruamel.yaml.comments.CommentedSeq(instruction_form["port_pressure"])
cs.fa.set_flow_style()
instruction_form["port_pressure"] = cs
dict_operands = []
for op in instruction_form["operands"]:
dict_operands.append(self.class_to_dict(op))
instruction_form["operands"] = dict_operands
formatted_instruction_forms.append(instruction_form)
# Replace load_throughput with styled version for RoundtripDumper
formatted_load_throughput = []
for lt in self._data["load_throughput"]:
cm = ruamel.yaml.comments.CommentedMap(lt)
cm = self.class_to_dict(lt[0])
cm["port_pressure"] = lt[1]
cm = ruamel.yaml.comments.CommentedMap(cm)
cm.fa.set_flow_style()
formatted_load_throughput.append(cm)
# Replace store_throughput with styled version for RoundtripDumper
formatted_store_throughput = []
for st in self._data["store_throughput"]:
cm = self.class_to_dict(st[0])
cm["port_pressure"] = st[1]
cm = ruamel.yaml.comments.CommentedMap(cm)
cm.fa.set_flow_style()
formatted_store_throughput.append(cm)
# Create YAML object
yaml = self._create_yaml_object()
if not stream:
@@ -342,12 +550,15 @@ class MachineModel(object):
"instruction_forms",
"instruction_forms_dict",
"load_throughput",
"store_throughput",
"internal_version",
]
},
stream,
)
yaml.dump({"load_throughput": formatted_load_throughput}, stream)
yaml.dump({"store_throughput": formatted_store_throughput}, stream)
yaml.dump({"instruction_forms": formatted_instruction_forms}, stream)
if isinstance(stream, StringIO):
@@ -449,48 +660,46 @@ class MachineModel(object):
operand_string += (
"s" if operand["scale"] == self.WILDCARD or operand["scale"] > 1 else ""
)
if "pre-indexed" in operand:
operand_string += "r" if operand["pre-indexed"] else ""
operand_string += "p" if operand["post-indexed"] else ""
if "pre_indexed" in operand:
operand_string += "r" if operand["pre_indexed"] else ""
operand_string += "p" if operand["post_indexed"] else ""
return operand_string
def _create_db_operand_aarch64(self, operand):
"""Create instruction form operand for DB out of operand string."""
if operand == "i":
return {"class": "immediate", "imd": "int"}
return ImmediateOperand(imd_type="int")
elif operand in "wxbhsdq":
return {"class": "register", "prefix": operand}
return RegisterOperand(prefix=operand)
elif operand.startswith("v"):
return {"class": "register", "prefix": "v", "shape": operand[1:2]}
return RegisterOperand(prefix="v", shape=operand[1:2])
elif operand.startswith("m"):
return {
"class": "memory",
"base": "x" if "b" in operand else None,
"offset": "imd" if "o" in operand else None,
"index": "gpr" if "i" in operand else None,
"scale": 8 if "s" in operand else 1,
"pre-indexed": True if "r" in operand else False,
"post-indexed": True if "p" in operand else False,
}
return MemoryOperand(
base="x" if "b" in operand else None,
offset="imd" if "o" in operand else None,
index="gpr" if "i" in operand else None,
scale=8 if "s" in operand else 1,
pre_indexed=True if "r" in operand else False,
post_indexed=True if "p" in operand else False,
)
else:
raise ValueError("Parameter {} is not a valid operand code".format(operand))
def _create_db_operand_x86(self, operand):
"""Create instruction form operand for DB out of operand string."""
if operand == "r":
return {"class": "register", "name": "gpr"}
return RegisterOperand(name="gpr")
elif operand in "xyz":
return {"class": "register", "name": operand + "mm"}
return RegisterOperand(name=operand + "mm")
elif operand == "i":
return {"class": "immediate", "imd": "int"}
return ImmediateOperand(imd_type="int")
elif operand.startswith("m"):
return {
"class": "memory",
"base": "gpr" if "b" in operand else None,
"offset": "imd" if "o" in operand else None,
"index": "gpr" if "i" in operand else None,
"scale": 8 if "s" in operand else 1,
}
return MemoryOperand(
base="gpr" if "b" in operand else None,
offset="imd" if "o" in operand else None,
index="gpr" if "i" in operand else None,
scale=8 if "s" in operand else 1,
)
else:
raise ValueError("Parameter {} is not a valid operand code".format(operand))
@@ -529,12 +738,8 @@ class MachineModel(object):
def _check_operands(self, i_operand, operand):
"""Check if the types of operand ``i_operand`` and ``operand`` match."""
# check for wildcard
if self.WILDCARD in operand:
if (
"class" in i_operand
and i_operand["class"] == "register"
or "register" in i_operand
):
if isinstance(operand, dict) and self.WILDCARD in operand:
if isinstance(i_operand, RegisterOperand):
return True
else:
return False
@@ -545,87 +750,87 @@ class MachineModel(object):
def _check_AArch64_operands(self, i_operand, operand):
"""Check if the types of operand ``i_operand`` and ``operand`` match."""
if "class" in operand:
# compare two DB entries
return self._compare_db_entries(i_operand, operand)
# if "class" in operand:
# compare two DB entries
# return self._compare_db_entries(i_operand, operand)
# TODO support class wildcards
# register
if "register" in operand:
if i_operand["class"] != "register":
if isinstance(operand, RegisterOperand):
if not isinstance(i_operand, RegisterOperand):
return False
return self._is_AArch64_reg_type(i_operand, operand["register"])
return self._is_AArch64_reg_type(i_operand, operand)
# memory
if "memory" in operand:
if i_operand["class"] != "memory":
if isinstance(operand, MemoryOperand):
if not isinstance(i_operand, MemoryOperand):
return False
return self._is_AArch64_mem_type(i_operand, operand["memory"])
return self._is_AArch64_mem_type(i_operand, operand)
# immediate
if i_operand["class"] == "immediate" and i_operand["imd"] == self.WILDCARD:
return "value" in operand or (
"immediate" in operand and "value" in operand["immediate"]
if isinstance(i_operand, ImmediateOperand) and i_operand.imd_type == self.WILDCARD:
return isinstance(operand, ImmediateOperand) and (operand.value is not None)
if isinstance(i_operand, ImmediateOperand) and i_operand.imd_type == "int":
return (
isinstance(operand, ImmediateOperand)
and operand.imd_type == "int"
and operand.value is not None
)
if i_operand["class"] == "immediate" and i_operand["imd"] == "int":
return ("value" in operand and operand.get("type", None) == "int") or (
"immediate" in operand
and "value" in operand["immediate"]
and operand["immediate"].get("type", None) == "int"
if isinstance(i_operand, ImmediateOperand) and i_operand.imd_type == "float":
return (
isinstance(operand, ImmediateOperand)
and operand.imd_type == "float"
and operand.value is not None
)
if i_operand["class"] == "immediate" and i_operand["imd"] == "float":
return ("float" in operand and operand.get("type", None) == "float") or (
"immediate" in operand
and "float" in operand["immediate"]
and operand["immediate"].get("type", None) == "float"
)
if i_operand["class"] == "immediate" and i_operand["imd"] == "double":
return ("double" in operand and operand.get("type", None) == "double") or (
"immediate" in operand
and "double" in operand["immediate"]
and operand["immediate"].get("type", None) == "double"
if isinstance(i_operand, ImmediateOperand) and i_operand.imd_type == "double":
return (
isinstance(operand, ImmediateOperand)
and operand.imd_type == "double"
and operand.value is not None
)
# identifier
if "identifier" in operand or (
"immediate" in operand and "identifier" in operand["immediate"]
if isinstance(operand, IdentifierOperand) or (
isinstance(operand, ImmediateOperand) and operand.identifier is not None
):
return i_operand["class"] == "identifier"
return isinstance(i_operand, IdentifierOperand)
# prefetch option
if "prfop" in operand:
return i_operand["class"] == "prfop"
if isinstance(operand, PrefetchOperand):
return isinstance(i_operand, PrefetchOperand)
# condition
if "condition" in operand:
if i_operand["class"] == "condition" and 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
def _check_x86_operands(self, i_operand, operand):
"""Check if the types of operand ``i_operand`` and ``operand`` match."""
if "class" in operand:
# compare two DB entries
return self._compare_db_entries(i_operand, operand)
# if "class" in operand.name:
# compare two DB entries
# return self._compare_db_entries(i_operand, operand)
# register
if "register" in operand:
if i_operand["class"] != "register":
if isinstance(operand, RegisterOperand):
if not isinstance(i_operand, RegisterOperand):
return False
return self._is_x86_reg_type(i_operand, operand["register"], consider_masking=False)
return self._is_x86_reg_type(i_operand, operand, consider_masking=False)
# memory
if "memory" in operand:
if i_operand["class"] != "memory":
if isinstance(operand, MemoryOperand):
if not isinstance(i_operand, MemoryOperand):
return False
return self._is_x86_mem_type(i_operand, operand["memory"])
return self._is_x86_mem_type(i_operand, operand)
# immediate
if "immediate" in operand or "value" in operand:
return i_operand["class"] == "immediate" and i_operand["imd"] == "int"
if isinstance(operand, ImmediateOperand):
# if "immediate" in operand.name or operand.value != None:
return isinstance(i_operand, ImmediateOperand) and i_operand.imd_type == "int"
# identifier (e.g., labels)
if "identifier" in operand:
return i_operand["class"] == "identifier"
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):
"""Check if operand types in DB format (i.e., not parsed) match."""
return True
operand_attributes = list(
filter(
lambda x: True if x != "source" and x != "destination" else False,
@@ -645,27 +850,26 @@ class MachineModel(object):
def _is_AArch64_reg_type(self, i_reg, reg):
"""Check if register type match."""
# check for wildcards
if reg["prefix"] == self.WILDCARD or i_reg["prefix"] == self.WILDCARD:
if "shape" in reg:
if "shape" in i_reg and (
reg["shape"] == i_reg["shape"]
or self.WILDCARD in (reg["shape"] + i_reg["shape"])
if reg.prefix == self.WILDCARD or i_reg.prefix == self.WILDCARD:
if reg.shape is not None:
if i_reg.shape is not None and (
reg.shape == i_reg.shape or self.WILDCARD in (reg.shape + i_reg.shape)
):
return True
return False
return True
# check for prefix and shape
if reg["prefix"] != i_reg["prefix"]:
if reg.prefix != i_reg.prefix:
return False
if "shape" in reg:
if "shape" in i_reg and (
reg["shape"] == i_reg["shape"] or self.WILDCARD in (reg["shape"] + i_reg["shape"])
if reg.shape is not None:
if i_reg.shape is not None and (
reg.shape == i_reg.shape or self.WILDCARD in (reg.shape + i_reg.shape)
):
return True
return False
if "lanes" in reg:
if "lanes" in i_reg and (
reg["lanes"] == i_reg["lanes"] or self.WILDCARD in (reg["lanes"] + i_reg["lanes"])
if reg.lanes is not None:
if i_reg.lanes is not None and (
reg.lanes == i_reg.lanes or self.WILDCARD in (reg.lanes + i_reg.lanes)
):
return True
return False
@@ -673,48 +877,52 @@ class MachineModel(object):
def _is_x86_reg_type(self, i_reg, reg, consider_masking=False):
"""Check if register type match."""
i_reg_name = i_reg["name"] if i_reg and "name" in i_reg else i_reg
if reg is None:
if i_reg is None:
return True
return False
if isinstance(i_reg, RegisterOperand):
i_reg_name = i_reg.name
else:
i_reg_name = i_reg
# check for wildcards
if i_reg_name == self.WILDCARD or reg["name"] == self.WILDCARD:
if isinstance(reg, str):
return False
if i_reg_name is None and reg.name is None:
return True
if i_reg_name == self.WILDCARD or reg.name == self.WILDCARD:
return True
# differentiate between vector registers (mm, xmm, ymm, zmm) and others (gpr)
parser_x86 = ParserX86ATT()
if parser_x86.is_vector_register(reg):
if reg["name"].rstrip(string.digits).lower() == i_reg_name:
if reg.name.rstrip(string.digits).lower() == i_reg_name:
# Consider masking and zeroing for AVX512
if consider_masking:
mask_ok = zero_ok = True
if "mask" in reg or "mask" in i_reg:
if reg.mask is not None or i_reg.mask is not None:
# one instruction is missing the masking while the other has it
mask_ok = False
# check for wildcard
if (
(
"mask" in reg
and reg["mask"].rstrip(string.digits).lower() == i_reg.get("mask")
reg.mask is not None
and reg.mask.rstrip(string.digits).lower() == i_reg.mask
)
or reg.get("mask") == self.WILDCARD
or i_reg.get("mask") == self.WILDCARD
or reg.mask == self.WILDCARD
or i_reg.mask == self.WILDCARD
):
mask_ok = True
if bool("zeroing" in reg) ^ bool("zeroing" in i_reg):
if bool(reg.zeroing) ^ bool("zeroing" in i_reg):
# one instruction is missing zeroing while the other has it
zero_ok = False
# check for wildcard
if (
i_reg.get("zeroing") == self.WILDCARD
or reg.get("zeroing") == self.WILDCARD
):
if i_reg.zeroing == self.WILDCARD or reg.zeroing == self.WILDCARD:
zero_ok = True
if not mask_ok or not zero_ok:
return False
return True
else:
if reg["name"].rstrip(string.digits).lower() == i_reg_name:
if reg.name.rstrip(string.digits).lower() == i_reg_name:
return True
if i_reg_name == "gpr":
return True
@@ -725,50 +933,48 @@ class MachineModel(object):
if (
# check base
(
(mem["base"] is None and i_mem["base"] is None)
or i_mem["base"] == self.WILDCARD
or mem["base"]["prefix"] == i_mem["base"]
(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))
)
# check offset
and (
mem["offset"] == i_mem["offset"]
or i_mem["offset"] == self.WILDCARD
mem.offset == i_mem.offset
or i_mem.offset == self.WILDCARD
or (
mem["offset"] is not None
and "identifier" in mem["offset"]
and i_mem["offset"] == "identifier"
mem.offset is not None
and isinstance(mem.offset, IdentifierOperand)
and isinstance(i_mem.offset, IdentifierOperand)
)
or (
mem["offset"] is not None
and "value" in mem["offset"]
and i_mem["offset"] == "imd"
mem.offset is not None
and isinstance(mem.offset, ImmediateOperand)
and i_mem.offset == "imd"
)
)
# check index
and (
mem["index"] == i_mem["index"]
or i_mem["index"] == self.WILDCARD
mem.index == i_mem.index
or i_mem.index == self.WILDCARD
or (
mem["index"] is not None
and "prefix" in mem["index"]
and mem["index"]["prefix"] == i_mem["index"]
mem.index is not None
and mem.index.prefix is not None
and mem.index.prefix == i_mem.index
)
)
# check scale
and (
mem["scale"] == i_mem["scale"]
or i_mem["scale"] == self.WILDCARD
or (mem["scale"] != 1 and i_mem["scale"] != 1)
mem.scale == i_mem.scale
or i_mem.scale == self.WILDCARD
or (mem.scale != 1 and i_mem.scale != 1)
)
# check pre-indexing
and (
i_mem["pre-indexed"] == self.WILDCARD
or ("pre_indexed" in mem) == (i_mem["pre-indexed"])
)
and (i_mem.pre_indexed == self.WILDCARD or mem.pre_indexed == i_mem.pre_indexed)
# check post-indexing
and (
i_mem["post-indexed"] == self.WILDCARD
or ("post_indexed" in mem) == (i_mem["post-indexed"])
i_mem.post_indexed == self.WILDCARD
or mem.post_indexed == i_mem.post_indexed
or (isinstance(mem.post_indexed, dict) and i_mem.post_indexed)
)
):
return True
@@ -779,48 +985,43 @@ class MachineModel(object):
if (
# check base
(
(mem["base"] is None and i_mem["base"] is None)
or i_mem["base"] == self.WILDCARD
or self._is_x86_reg_type(i_mem["base"], mem["base"])
(mem.base is None and i_mem.base is None)
or i_mem.base == self.WILDCARD
or self._is_x86_reg_type(i_mem.base, mem.base)
)
# check offset
and (
mem["offset"] == i_mem["offset"]
or i_mem["offset"] == self.WILDCARD
mem.offset == i_mem.offset
or i_mem.offset == self.WILDCARD
or (
mem["offset"] is not None
and "identifier" in mem["offset"]
and i_mem["offset"] == "identifier"
mem.offset is not None
and isinstance(mem.offset, IdentifierOperand)
and isinstance(i_mem.offset, IdentifierOperand)
)
or (
mem["offset"] is not None
and "value" in mem["offset"]
mem.offset is not None
and isinstance(mem.offset, ImmediateOperand)
and (
i_mem["offset"] == "imd"
or (i_mem["offset"] is None and mem["offset"]["value"] == "0")
i_mem.offset == "imd" or (i_mem.offset is None and mem.offset.value == "0")
)
)
or (
mem["offset"] is not None
and "identifier" in mem["offset"]
and i_mem["offset"] == "id"
)
or (isinstance(mem.offset, IdentifierOperand) and i_mem.offset == "id")
)
# check index
and (
mem["index"] == i_mem["index"]
or i_mem["index"] == self.WILDCARD
mem.index == i_mem.index
or i_mem.index == self.WILDCARD
or (
mem["index"] is not None
and "name" in mem["index"]
and self._is_x86_reg_type(i_mem["index"], mem["index"])
mem.index is not None
# and mem.index.name != None
and self._is_x86_reg_type(i_mem.index, mem.index)
)
)
# check scale
and (
mem["scale"] == i_mem["scale"]
or i_mem["scale"] == self.WILDCARD
or (mem["scale"] != 1 and i_mem["scale"] != 1)
mem.scale == i_mem.scale
or i_mem.scale == self.WILDCARD
or (mem.scale != 1 and i_mem.scale != 1)
)
):
return True