mirror of
https://github.com/RRZE-HPC/OSACA.git
synced 2025-12-16 09:00:05 +01:00
Added 2 operand types and made changes for attribute usage
This commit is contained in:
0
osaca/parser/attr_dict.py
Executable file → Normal file
0
osaca/parser/attr_dict.py
Executable file → Normal file
0
osaca/parser/base_parser.py
Executable file → Normal file
0
osaca/parser/base_parser.py
Executable file → Normal file
31
osaca/parser/identifier.py
Normal file
31
osaca/parser/identifier.py
Normal file
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from osaca.parser.operand import Operand
|
||||
|
||||
class IdentifierOperand(Operand):
|
||||
def __init__(self, name, OFFSET = None, RELOCATION = None):
|
||||
super().__init__(name)
|
||||
self._OFFSET = OFFSET
|
||||
self._RELOCATION = RELOCATION
|
||||
|
||||
@property
|
||||
def offset(self):
|
||||
return self._OFFSET
|
||||
|
||||
@offset.setter
|
||||
def offset(self, offset):
|
||||
self._OFFSET = offset
|
||||
|
||||
@property
|
||||
def relocation(self):
|
||||
return self._RELOCATION
|
||||
|
||||
@relocation.setter
|
||||
def relocation(self, relocation):
|
||||
self._RELOCATION = relocation
|
||||
|
||||
def __str__(self):
|
||||
return 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})"
|
||||
@@ -28,7 +28,18 @@ class InstructionForm:
|
||||
self._LABEL_ID = LABEL_ID
|
||||
self._LINE = LINE
|
||||
self._LINE_NUMBER = LINE_NUMBER
|
||||
|
||||
self._SEMANTIC_OPERANDS = SEMANTIC_OPERANDS
|
||||
self._UOPS = None
|
||||
#self.semantic_operands = {"source": [], "destination": [], "src_dst": []}
|
||||
self._LATENCY = None
|
||||
self._THROUGHPUT = None
|
||||
self._LATENCY_CP = []
|
||||
self._LATENCY_LCD = []
|
||||
self._LATENCY_WO_LOAD = None
|
||||
self._PORT_PRESSURE = []
|
||||
self._PORT_UOPS = []
|
||||
self._FLAGS = []
|
||||
|
||||
@property
|
||||
def semantic_operands(self):
|
||||
@@ -62,6 +73,18 @@ class InstructionForm:
|
||||
def operands(self):
|
||||
return self._OPERANDS_ID
|
||||
|
||||
@property
|
||||
def port_pressure(self):
|
||||
return self._PORT_PRESSURE
|
||||
|
||||
@property
|
||||
def port_uops(self):
|
||||
return self._PORT_UOPS
|
||||
|
||||
@property
|
||||
def flags(self):
|
||||
return self._FLAGS
|
||||
|
||||
@semantic_operands.setter
|
||||
def semantic_operands(self, semantic_operands):
|
||||
self._SEMANTIC_OPERANDS = semantic_operands
|
||||
@@ -93,7 +116,19 @@ class InstructionForm:
|
||||
@comment.setter
|
||||
def comment(self, comment):
|
||||
self._COMMENT_ID =comment
|
||||
|
||||
@port_pressure.setter
|
||||
def port_pressure(self, port_pressure):
|
||||
self._PORT_PRESSURE = port_pressure
|
||||
|
||||
@port_uops.setter
|
||||
def port_uops(self, port_uops):
|
||||
self._PORT_UOPS = port_uops
|
||||
|
||||
@flags.setter
|
||||
def flags(self, flags):
|
||||
self._FLAGS = flags
|
||||
|
||||
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})"
|
||||
|
||||
|
||||
61
osaca/parser/parser_AArch64.py
Executable file → Normal file
61
osaca/parser/parser_AArch64.py
Executable file → Normal file
@@ -8,6 +8,8 @@ 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
|
||||
|
||||
class ParserAArch64(BaseParser):
|
||||
@@ -288,11 +290,10 @@ class ParserAArch64(BaseParser):
|
||||
if result is None:
|
||||
try:
|
||||
result = self.process_operand(self.label.parseString(line, parseAll=True).asDict())
|
||||
result = AttrDict.convert_dict(result)
|
||||
instruction_form.label = result[self.LABEL_ID].name
|
||||
if self.COMMENT_ID in result[self.LABEL_ID]:
|
||||
instruction_form.label = result.name
|
||||
if result.comment != None:
|
||||
instruction_form.comment= " ".join(
|
||||
result[self.LABEL_ID][self.COMMENT_ID]
|
||||
result.comment
|
||||
)
|
||||
except pp.ParseException:
|
||||
pass
|
||||
@@ -325,9 +326,9 @@ class ParserAArch64(BaseParser):
|
||||
raise ValueError(
|
||||
"Unable to parse {!r} on line {}".format(line, line_number)
|
||||
) from e
|
||||
instruction_form.instruction = result[self.INSTRUCTION_ID]
|
||||
instruction_form.operands = result[self.OPERANDS_ID]
|
||||
instruction_form.comment = result[self.COMMENT_ID]
|
||||
instruction_form.instruction = result.instruction
|
||||
instruction_form.operands = result.operands
|
||||
instruction_form.comment = result.comment
|
||||
|
||||
return instruction_form
|
||||
|
||||
@@ -339,7 +340,6 @@ class ParserAArch64(BaseParser):
|
||||
:returns: `dict` -- parsed instruction form
|
||||
"""
|
||||
result = self.instruction_parser.parseString(instruction, parseAll=True).asDict()
|
||||
result = AttrDict.convert_dict(result)
|
||||
operands = []
|
||||
# Add operands to list
|
||||
# Check first operand
|
||||
@@ -362,15 +362,12 @@ class ParserAArch64(BaseParser):
|
||||
if "operand5" in result:
|
||||
operand = self.process_operand(result["operand5"])
|
||||
operands.extend(operand) if isinstance(operand, list) else operands.append(operand)
|
||||
|
||||
return_dict = AttrDict(
|
||||
{
|
||||
self.INSTRUCTION_ID: result.mnemonic,
|
||||
self.OPERANDS_ID: operands,
|
||||
self.COMMENT_ID: " ".join(result[self.COMMENT_ID])
|
||||
return_dict = InstructionForm(
|
||||
INSTRUCTION_ID = result['mnemonic'],
|
||||
OPERANDS_ID = operands,
|
||||
COMMENT_ID = " ".join(result[self.COMMENT_ID])
|
||||
if self.COMMENT_ID in result
|
||||
else None,
|
||||
}
|
||||
)
|
||||
return return_dict
|
||||
|
||||
@@ -416,30 +413,31 @@ 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 = AttrDict({"offset": offset, "base": base, "index": index, "scale": scale})
|
||||
new_dict = MemoryOperand(OFFSET_ID = offset, BASE_ID = base, INDEX_ID = index, SCALE_ID = scale)
|
||||
if "pre_indexed" in memory_address:
|
||||
new_dict["pre_indexed"] = True
|
||||
new_dict.pre_indexed = True
|
||||
if "post_indexed" in memory_address:
|
||||
if "value" in memory_address["post_indexed"]:
|
||||
new_dict["post_indexed"] = {
|
||||
new_dict.post_indexed = {
|
||||
"value": int(memory_address["post_indexed"]["value"], 0)
|
||||
}
|
||||
else:
|
||||
new_dict["post_indexed"] = memory_address["post_indexed"]
|
||||
return AttrDict({self.MEMORY_ID: new_dict})
|
||||
new_dict.post_indexed = memory_address["post_indexed"]
|
||||
return new_dict
|
||||
|
||||
def process_sp_register(self, register):
|
||||
"""Post-process stack pointer register"""
|
||||
reg = register
|
||||
reg["prefix"] = "x"
|
||||
return AttrDict({self.REGISTER_ID: reg})
|
||||
new_reg = RegisterOperand(PREFIX_ID = "x")
|
||||
#reg["prefix"] = "x"
|
||||
return new_reg
|
||||
|
||||
def resolve_range_list(self, operand):
|
||||
"""
|
||||
Resolve range or list register operand to list of registers.
|
||||
Returns None if neither list nor range
|
||||
"""
|
||||
if "register" in operand:
|
||||
if "register" in operand.name:
|
||||
if "list" in operand.register:
|
||||
index = operand.register.get("index")
|
||||
range_list = []
|
||||
@@ -447,7 +445,7 @@ class ParserAArch64(BaseParser):
|
||||
reg = deepcopy(reg)
|
||||
if index is not None:
|
||||
reg["index"] = int(index, 0)
|
||||
range_list.append(AttrDict({self.REGISTER_ID: reg}))
|
||||
range_list.append(reg)
|
||||
return range_list
|
||||
elif "range" in operand.register:
|
||||
base_register = operand.register.range[0]
|
||||
@@ -460,7 +458,7 @@ class ParserAArch64(BaseParser):
|
||||
if index is not None:
|
||||
reg["index"] = int(index, 0)
|
||||
reg["name"] = str(name)
|
||||
range_list.append(AttrDict({self.REGISTER_ID: reg}))
|
||||
range_list.append(reg)
|
||||
return range_list
|
||||
# neither register list nor range, return unmodified
|
||||
return operand
|
||||
@@ -513,19 +511,14 @@ class ParserAArch64(BaseParser):
|
||||
return AttrDict({self.IMMEDIATE_ID: immediate})
|
||||
else:
|
||||
# change 'mantissa' key to 'value'
|
||||
return AttrDict(
|
||||
{
|
||||
self.IMMEDIATE_ID: AttrDict(
|
||||
{"value": immediate[dict_name]["mantissa"], "type": 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"]
|
||||
return AttrDict({self.LABEL_ID: label})
|
||||
#label["name"] = label["name"]["name"]
|
||||
new_label = LabelOperand(NAME_ID = label["name"]["name"])
|
||||
return new_label
|
||||
|
||||
def process_identifier(self, identifier):
|
||||
"""Post-process identifier operand"""
|
||||
|
||||
3
osaca/parser/parser_x86att.py
Executable file → Normal file
3
osaca/parser/parser_x86att.py
Executable file → Normal file
@@ -11,6 +11,9 @@ 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
|
||||
|
||||
class ParserX86ATT(BaseParser):
|
||||
_instance = None
|
||||
|
||||
87
osaca/parser/register.py
Normal file
87
osaca/parser/register.py
Normal file
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from osaca.parser.operand import Operand
|
||||
|
||||
class RegisterOperand(Operand):
|
||||
def __init__(self, NAME_ID = None, WIDTH_ID = None, PREFIX_ID = None, REG_ID = None
|
||||
, REGTYPE_ID = None, LANES = None, SHAPE = None, INDEX = False
|
||||
, MASK = False, ZEROING = False):
|
||||
super().__init__(NAME_ID)
|
||||
self._WIDTH_ID = WIDTH_ID
|
||||
self._PREFIX_ID = PREFIX_ID
|
||||
self._REG_ID = REG_ID
|
||||
self._LANES = LANES
|
||||
self._SHAPE = SHAPE
|
||||
self._INDEX = INDEX
|
||||
self._MASK = MASK
|
||||
self._ZEROING = ZEROING
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
return self._WIDTH_ID
|
||||
|
||||
@width.setter
|
||||
def width(self, width):
|
||||
self._WIDTH_ID = width
|
||||
|
||||
@property
|
||||
def prefix(self):
|
||||
return self._PREFIX_ID
|
||||
|
||||
@prefix.setter
|
||||
def prefix(self, prefix):
|
||||
self._PREFIX = prefix
|
||||
|
||||
@property
|
||||
def reg_id(self):
|
||||
return self._REG_ID
|
||||
|
||||
@reg_id.setter
|
||||
def reg_id(self, reg_id):
|
||||
self._REG_ID = reg_id
|
||||
|
||||
@property
|
||||
def lanes(self):
|
||||
return self._LANES
|
||||
|
||||
@lanes.setter
|
||||
def lanes(self, lanes):
|
||||
self._LANES = lanes
|
||||
|
||||
@property
|
||||
def shape(self):
|
||||
return self._SHAPE
|
||||
|
||||
@shape.setter
|
||||
def shape(self, shape):
|
||||
self._SHAPE = shape
|
||||
|
||||
@property
|
||||
def index(self):
|
||||
return self._INDEX
|
||||
|
||||
@index.setter
|
||||
def index(self, index):
|
||||
self._INDEX = index
|
||||
|
||||
@property
|
||||
def mask(self):
|
||||
return self._MASK
|
||||
|
||||
@mask.setter
|
||||
def mask(self, mask):
|
||||
self._MASK = mask
|
||||
|
||||
@property
|
||||
def zeroing(self):
|
||||
return self._ZEROING
|
||||
|
||||
@zeroing.setter
|
||||
def zeroing(self, zeroing):
|
||||
self._ZEROING = zeroing
|
||||
|
||||
def __str__(self):
|
||||
return f"MemoryOperand({self.width_id}, {self.prefix_id}, {self.reg_id}, {self.lanes}, {self.shape}, {self.index}, {self.mask}, {self.zeroing})"
|
||||
|
||||
def __repr__(self):
|
||||
return f"MemoryOperand(width_id={self.width_id}, prefix_id={self.prefix_id}, reg_id={self.reg_id}, lanes={self.lanes}, shape={self.shape}, index={self.index}, mask={self.mask}, zeroing={self.zeroing})"
|
||||
134
osaca/semantics/arch_semantics.py
Executable file → Normal file
134
osaca/semantics/arch_semantics.py
Executable file → Normal file
@@ -46,10 +46,10 @@ class ArchSemantics(ISASemantics):
|
||||
for idx, instruction_form in enumerate(kernel[start:], start):
|
||||
multiple_assignments = False
|
||||
# if iform has multiple possible port assignments, check all in a DFS manner and take the best
|
||||
if isinstance(instruction_form["port_uops"], dict):
|
||||
if isinstance(instruction_form.port_uops, dict):
|
||||
best_kernel = None
|
||||
best_kernel_tp = sys.maxsize
|
||||
for port_util_alt in list(instruction_form["port_uops"].values())[1:]:
|
||||
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(
|
||||
@@ -62,15 +62,15 @@ 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]
|
||||
for uop in instruction_form["port_uops"]:
|
||||
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])
|
||||
indices = [port_list.index(p) for p in ports]
|
||||
# check if port sum of used ports for uop are unbalanced
|
||||
port_sums = self._to_list(itemgetter(*indices)(self.get_throughput_sum(kernel)))
|
||||
instr_ports = self._to_list(
|
||||
itemgetter(*indices)(instruction_form["port_pressure"])
|
||||
itemgetter(*indices)(instruction_form.port_pressure)
|
||||
)
|
||||
if len(set(port_sums)) > 1:
|
||||
# balance ports
|
||||
@@ -87,7 +87,7 @@ class ArchSemantics(ISASemantics):
|
||||
differences[max_port_idx] -= INC
|
||||
differences[min_port_idx] += INC
|
||||
# instr_ports = [round(p, 2) for p in instr_ports]
|
||||
self._itemsetter(*indices)(instruction_form["port_pressure"], *instr_ports)
|
||||
self._itemsetter(*indices)(instruction_form.port_pressure, *instr_ports)
|
||||
# check if min port is zero
|
||||
if round(min(instr_ports), 2) <= 0:
|
||||
# if port_pressure is not exactly 0.00, add the residual to
|
||||
@@ -100,21 +100,21 @@ class ArchSemantics(ISASemantics):
|
||||
# delete it
|
||||
del differences[instr_ports.index(min(instr_ports))]
|
||||
self._itemsetter(*indices)(
|
||||
instruction_form["port_pressure"], *instr_ports
|
||||
instruction_form.port_pressure, *instr_ports
|
||||
)
|
||||
zero_index = [
|
||||
p
|
||||
for p in indices
|
||||
if round(instruction_form["port_pressure"][p], 2) == 0
|
||||
or instruction_form["port_pressure"][p] < 0.00
|
||||
if round(instruction_form.port_pressure[p], 2) == 0
|
||||
or instruction_form.port_pressure[p] < 0.00
|
||||
][0]
|
||||
instruction_form["port_pressure"][zero_index] = 0.0
|
||||
instruction_form.port_pressure[zero_index] = 0.0
|
||||
# Remove from further balancing
|
||||
indices = [
|
||||
p for p in indices if instruction_form["port_pressure"][p] > 0
|
||||
p for p in indices if instruction_form.port_pressure[p] > 0
|
||||
]
|
||||
instr_ports = self._to_list(
|
||||
itemgetter(*indices)(instruction_form["port_pressure"])
|
||||
itemgetter(*indices)(instruction_form.port_pressure)
|
||||
)
|
||||
# never remove more than the fixed utilization per uop and port, i.e.,
|
||||
# cycles/len(ports)
|
||||
@@ -124,7 +124,7 @@ class ArchSemantics(ISASemantics):
|
||||
# pressure is not 0
|
||||
del indices[differences.index(min(differences))]
|
||||
instr_ports = self._to_list(
|
||||
itemgetter(*indices)(instruction_form["port_pressure"])
|
||||
itemgetter(*indices)(instruction_form.port_pressure)
|
||||
)
|
||||
del differences[differences.index(min(differences))]
|
||||
port_sums = self._to_list(
|
||||
@@ -139,14 +139,14 @@ class ArchSemantics(ISASemantics):
|
||||
|
||||
def set_hidden_loads(self, kernel):
|
||||
"""Hide loads behind stores if architecture supports hidden loads (depricated)"""
|
||||
loads = [instr for instr in kernel if INSTR_FLAGS.HAS_LD in instr["flags"]]
|
||||
stores = [instr for instr in kernel if INSTR_FLAGS.HAS_ST in instr["flags"]]
|
||||
loads = [instr for instr in kernel if INSTR_FLAGS.HAS_LD in instr.flags]
|
||||
stores = [instr for instr in kernel if INSTR_FLAGS.HAS_ST in instr.flags]
|
||||
# Filter instructions including load and store
|
||||
load_ids = [instr["line_number"] for instr in loads]
|
||||
store_ids = [instr["line_number"] for instr in stores]
|
||||
load_ids = [instr.line_number for instr in loads]
|
||||
store_ids = [instr.line_number for instr in stores]
|
||||
shared_ldst = list(set(load_ids).intersection(set(store_ids)))
|
||||
loads = [instr for instr in loads if instr["line_number"] not in shared_ldst]
|
||||
stores = [instr for instr in stores if instr["line_number"] not in shared_ldst]
|
||||
loads = [instr for instr in loads if instr.line_number not in shared_ldst]
|
||||
stores = [instr for instr in stores if instr.line_number not in shared_ldst]
|
||||
|
||||
if len(stores) == 0 or len(loads) == 0:
|
||||
# nothing to do
|
||||
@@ -182,35 +182,35 @@ class ArchSemantics(ISASemantics):
|
||||
"""Assign throughput and latency to an instruction form."""
|
||||
flags = []
|
||||
port_number = len(self._machine_model["ports"])
|
||||
if instruction_form["instruction"] is None:
|
||||
if instruction_form.instruction is None:
|
||||
# No instruction (label, comment, ...) --> ignore
|
||||
throughput = 0.0
|
||||
latency = 0.0
|
||||
latency_wo_load = latency
|
||||
instruction_form["port_pressure"] = [0.0 for i in range(port_number)]
|
||||
instruction_form["port_uops"] = []
|
||||
instruction_form.port_pressure = [0.0 for i in range(port_number)]
|
||||
instruction_form.port_uops = []
|
||||
else:
|
||||
instruction_data = self._machine_model.get_instruction(
|
||||
instruction_form["instruction"], instruction_form["operands"]
|
||||
instruction_form.instruction, instruction_form.operands
|
||||
)
|
||||
if (
|
||||
not instruction_data
|
||||
and self._isa == "x86"
|
||||
and instruction_form["instruction"][-1] in self.GAS_SUFFIXES
|
||||
and instruction_form.instruction[-1] in self.GAS_SUFFIXES
|
||||
):
|
||||
# check for instruction without GAS suffix
|
||||
instruction_data = self._machine_model.get_instruction(
|
||||
instruction_form["instruction"][:-1], instruction_form["operands"]
|
||||
instruction_form.instruction[:-1], instruction_form.operands
|
||||
)
|
||||
if (
|
||||
instruction_data is None
|
||||
and self._isa == "aarch64"
|
||||
and "." in instruction_form["instruction"]
|
||||
and "." in instruction_form.instruction
|
||||
):
|
||||
# Check for instruction without shape/cc suffix
|
||||
suffix_start = instruction_form["instruction"].index(".")
|
||||
suffix_start = instruction_form.instruction.index(".")
|
||||
instruction_data = self._machine_model.get_instruction(
|
||||
instruction_form["instruction"][:suffix_start], instruction_form["operands"]
|
||||
instruction_form.instruction[:suffix_start], instruction_form.operands
|
||||
)
|
||||
if instruction_data:
|
||||
# instruction form in DB
|
||||
@@ -227,33 +227,33 @@ class ArchSemantics(ISASemantics):
|
||||
assign_unknown = True
|
||||
# check for equivalent register-operands DB entry if LD
|
||||
if (
|
||||
INSTR_FLAGS.HAS_LD in instruction_form["flags"]
|
||||
or INSTR_FLAGS.HAS_ST in instruction_form["flags"]
|
||||
INSTR_FLAGS.HAS_LD in instruction_form.flags
|
||||
or INSTR_FLAGS.HAS_ST in instruction_form.flags
|
||||
):
|
||||
# dynamically combine LD/ST and reg form of instruction form
|
||||
# substitute mem and look for reg-only variant
|
||||
operands = self.substitute_mem_address(instruction_form["operands"])
|
||||
operands = self.substitute_mem_address(instruction_form.operands)
|
||||
instruction_data_reg = self._machine_model.get_instruction(
|
||||
instruction_form["instruction"], operands
|
||||
instruction_form.instruction, operands
|
||||
)
|
||||
if (
|
||||
not instruction_data_reg
|
||||
and self._isa == "x86"
|
||||
and instruction_form["instruction"][-1] in self.GAS_SUFFIXES
|
||||
and instruction_form.instruction[-1] in self.GAS_SUFFIXES
|
||||
):
|
||||
# check for instruction without GAS suffix
|
||||
instruction_data_reg = self._machine_model.get_instruction(
|
||||
instruction_form["instruction"][:-1], operands
|
||||
instruction_form.instruction[:-1], operands
|
||||
)
|
||||
if (
|
||||
instruction_data_reg is None
|
||||
and self._isa == "aarch64"
|
||||
and "." in instruction_form["instruction"]
|
||||
and "." in instruction_form.instruction
|
||||
):
|
||||
# Check for instruction without shape/cc suffix
|
||||
suffix_start = instruction_form["instruction"].index(".")
|
||||
suffix_start = instruction_form.instruction.index(".")
|
||||
instruction_data_reg = self._machine_model.get_instruction(
|
||||
instruction_form["instruction"][:suffix_start], operands
|
||||
instruction_form.instruction[:suffix_start], operands
|
||||
)
|
||||
if instruction_data_reg:
|
||||
assign_unknown = False
|
||||
@@ -265,13 +265,13 @@ class ArchSemantics(ISASemantics):
|
||||
dummy_reg = {"class": "register", "name": reg_type}
|
||||
data_port_pressure = [0.0 for _ in range(port_number)]
|
||||
data_port_uops = []
|
||||
if INSTR_FLAGS.HAS_LD in instruction_form["flags"]:
|
||||
if INSTR_FLAGS.HAS_LD in instruction_form.flags:
|
||||
# LOAD performance data
|
||||
load_perf_data = self._machine_model.get_load_throughput(
|
||||
[
|
||||
x["memory"]
|
||||
for x in instruction_form["semantic_operands"]["source"]
|
||||
+ instruction_form["semantic_operands"]["src_dst"]
|
||||
for x in instruction_form.semantic_operands["source"]
|
||||
+ instruction_form.semantic_operands["src_dst"]
|
||||
if "memory" in x
|
||||
][0]
|
||||
)
|
||||
@@ -296,11 +296,11 @@ class ArchSemantics(ISASemantics):
|
||||
reg_type
|
||||
]
|
||||
data_port_pressure = [pp * multiplier for pp in data_port_pressure]
|
||||
if INSTR_FLAGS.HAS_ST in instruction_form["flags"]:
|
||||
if INSTR_FLAGS.HAS_ST in instruction_form.flags:
|
||||
# STORE performance data
|
||||
destinations = (
|
||||
instruction_form["semantic_operands"]["destination"]
|
||||
+ instruction_form["semantic_operands"]["src_dst"]
|
||||
instruction_form.semantic_operands["destination"]
|
||||
+ instruction_form.semantic_operands["src_dst"]
|
||||
)
|
||||
store_perf_data = self._machine_model.get_store_throughput(
|
||||
[x["memory"] for x in destinations if "memory" in x][0], dummy_reg
|
||||
@@ -314,18 +314,18 @@ class ArchSemantics(ISASemantics):
|
||||
if (
|
||||
self._isa == "aarch64"
|
||||
and "memory"
|
||||
not in instruction_form["semantic_operands"]["destination"]
|
||||
not in instruction_form.semantic_operands["destination"]
|
||||
and all(
|
||||
[
|
||||
"post_indexed" in op["memory"]
|
||||
or "pre_indexed" in op["memory"]
|
||||
for op in instruction_form["semantic_operands"]["src_dst"]
|
||||
for op in instruction_form.semantic_operands["src_dst"]
|
||||
if "memory" in op
|
||||
]
|
||||
)
|
||||
):
|
||||
st_data_port_uops = []
|
||||
instruction_form["flags"].remove(INSTR_FLAGS.HAS_ST)
|
||||
instruction_form.flags.remove(INSTR_FLAGS.HAS_ST)
|
||||
|
||||
# sum up all data ports in case for LOAD and STORE
|
||||
st_data_port_pressure = self._machine_model.average_port_pressure(
|
||||
@@ -349,12 +349,12 @@ class ArchSemantics(ISASemantics):
|
||||
# Add LD and ST latency
|
||||
latency += (
|
||||
self._machine_model.get_load_latency(reg_type)
|
||||
if INSTR_FLAGS.HAS_LD in instruction_form["flags"]
|
||||
if INSTR_FLAGS.HAS_LD in instruction_form.flags
|
||||
else 0
|
||||
)
|
||||
latency += (
|
||||
self._machine_model.get_store_latency(reg_type)
|
||||
if INSTR_FLAGS.HAS_ST in instruction_form["flags"]
|
||||
if INSTR_FLAGS.HAS_ST in instruction_form.flags
|
||||
else 0
|
||||
)
|
||||
latency_wo_load = instruction_data_reg["latency"]
|
||||
@@ -367,13 +367,13 @@ class ArchSemantics(ISASemantics):
|
||||
# [
|
||||
# 'post_indexed' in op['memory'] or
|
||||
# 'pre_indexed' in op['memory']
|
||||
# for op in instruction_form['operands']
|
||||
# for op in instruction_form.operands
|
||||
# if 'memory' in op
|
||||
# ]
|
||||
# )
|
||||
# ):
|
||||
# latency_wo_load = 1.0
|
||||
instruction_form["port_pressure"] = [
|
||||
instruction_form.port_pressure = [
|
||||
sum(x)
|
||||
for x in zip(
|
||||
data_port_pressure,
|
||||
@@ -382,7 +382,7 @@ class ArchSemantics(ISASemantics):
|
||||
),
|
||||
)
|
||||
]
|
||||
instruction_form["port_uops"] = list(
|
||||
instruction_form.port_uops = list(
|
||||
chain(instruction_data_reg["port_pressure"], data_port_uops)
|
||||
)
|
||||
|
||||
@@ -391,21 +391,21 @@ class ArchSemantics(ISASemantics):
|
||||
throughput = 0.0
|
||||
latency = 0.0
|
||||
latency_wo_load = latency
|
||||
instruction_form["port_pressure"] = [0.0 for i in range(port_number)]
|
||||
instruction_form["port_uops"] = []
|
||||
instruction_form.port_pressure = [0.0 for i in range(port_number)]
|
||||
instruction_formport_uops = []
|
||||
flags += [INSTR_FLAGS.TP_UNKWN, INSTR_FLAGS.LT_UNKWN]
|
||||
# flatten flag list
|
||||
flags = list(set(flags))
|
||||
if "flags" not in instruction_form:
|
||||
instruction_form["flags"] = flags
|
||||
if instruction_form.flags == []:
|
||||
instruction_form.flags = flags
|
||||
else:
|
||||
instruction_form["flags"] += flags
|
||||
instruction_form["throughput"] = throughput
|
||||
instruction_form["latency"] = latency
|
||||
instruction_form["latency_wo_load"] = latency_wo_load
|
||||
instruction_form.flags += flags
|
||||
instruction_form.throughput = throughput
|
||||
instruction_form.latency = latency
|
||||
instruction_form.latency_wo_load = latency_wo_load
|
||||
# for later CP and loop-carried dependency analysis
|
||||
instruction_form["latency_cp"] = 0
|
||||
instruction_form["latency_lcd"] = 0
|
||||
instruction_form.latency_cp = 0
|
||||
instruction_form.latency_lcd = 0
|
||||
|
||||
def _handle_instruction_found(self, instruction_data, port_number, instruction_form, flags):
|
||||
"""Apply performance data to instruction if it was found in the archDB"""
|
||||
@@ -413,11 +413,11 @@ class ArchSemantics(ISASemantics):
|
||||
port_pressure = self._machine_model.average_port_pressure(
|
||||
instruction_data["port_pressure"]
|
||||
)
|
||||
instruction_form["port_uops"] = instruction_data["port_pressure"]
|
||||
instruction_form.port_uops = instruction_data["port_pressure"]
|
||||
try:
|
||||
assert isinstance(port_pressure, list)
|
||||
assert len(port_pressure) == port_number
|
||||
instruction_form["port_pressure"] = port_pressure
|
||||
instruction_form.port_pressure = port_pressure
|
||||
if sum(port_pressure) == 0 and throughput is not None:
|
||||
# port pressure on all ports 0 --> not bound to a port
|
||||
flags.append(INSTR_FLAGS.NOT_BOUND)
|
||||
@@ -426,8 +426,8 @@ class ArchSemantics(ISASemantics):
|
||||
"Port pressure could not be imported correctly from database. "
|
||||
+ "Please check entry for:\n {}".format(instruction_form)
|
||||
)
|
||||
instruction_form["port_pressure"] = [0.0 for i in range(port_number)]
|
||||
instruction_form["port_uops"] = []
|
||||
instruction_form.port_pressure = [0.0 for i in range(port_number)]
|
||||
instruction_form.port_uops = []
|
||||
flags.append(INSTR_FLAGS.TP_UNKWN)
|
||||
if throughput is None:
|
||||
# assume 0 cy and mark as unknown
|
||||
@@ -440,7 +440,7 @@ class ArchSemantics(ISASemantics):
|
||||
latency = 0.0
|
||||
latency_wo_load = latency
|
||||
flags.append(INSTR_FLAGS.LT_UNKWN)
|
||||
if INSTR_FLAGS.HAS_LD in instruction_form["flags"]:
|
||||
if INSTR_FLAGS.HAS_LD in instruction_form.flags:
|
||||
flags.append(INSTR_FLAGS.LD)
|
||||
return throughput, port_pressure, latency, latency_wo_load
|
||||
|
||||
@@ -489,7 +489,7 @@ class ArchSemantics(ISASemantics):
|
||||
"""Get the overall throughput sum separated by port of all instructions of a kernel."""
|
||||
# ignoring all lines with throughput == 0.0, because there won't be anything to sum up
|
||||
# typically comment, label and non-instruction lines
|
||||
port_pressures = [instr["port_pressure"] for instr in kernel if instr["throughput"] != 0.0]
|
||||
port_pressures = [instr.port_pressure for instr in kernel if instr.throughput != 0.0]
|
||||
# Essentially summing up each columns of port_pressures, where each column is one port
|
||||
# and each row is one line of the kernel
|
||||
# round is necessary to ensure termination of ArchsSemantics.assign_optimal_throughput
|
||||
|
||||
16
osaca/semantics/hw_model.py
Executable file → Normal file
16
osaca/semantics/hw_model.py
Executable file → Normal file
@@ -526,7 +526,7 @@ 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 self.WILDCARD in operand.name:
|
||||
if (
|
||||
"class" in i_operand
|
||||
and i_operand["class"] == "register"
|
||||
@@ -601,24 +601,24 @@ class MachineModel(object):
|
||||
|
||||
def _check_x86_operands(self, i_operand, operand):
|
||||
"""Check if the types of operand ``i_operand`` and ``operand`` match."""
|
||||
if "class" in operand:
|
||||
if "class" in operand.name:
|
||||
# compare two DB entries
|
||||
return self._compare_db_entries(i_operand, operand)
|
||||
# register
|
||||
if "register" in operand:
|
||||
if "register" in operand.name:
|
||||
if i_operand["class"] != "register":
|
||||
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 "memory" in operand.name:
|
||||
if i_operand["class"] != "memory":
|
||||
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:
|
||||
if "immediate" in operand.name or operand.value != None:
|
||||
return i_operand["class"] == "immediate" and i_operand["imd"] == "int"
|
||||
# identifier (e.g., labels)
|
||||
if "identifier" in operand:
|
||||
if "identifier" in operand.name:
|
||||
return i_operand["class"] == "identifier"
|
||||
|
||||
def _compare_db_entries(self, operand_1, operand_2):
|
||||
|
||||
14
osaca/semantics/isa_semantics.py
Executable file → Normal file
14
osaca/semantics/isa_semantics.py
Executable file → Normal file
@@ -3,6 +3,7 @@ from itertools import chain
|
||||
|
||||
from osaca import utils
|
||||
from osaca.parser import AttrDict, ParserAArch64, ParserX86ATT
|
||||
from osaca.parser.memory import MemoryOperand
|
||||
|
||||
from .hw_model import MachineModel
|
||||
|
||||
@@ -110,7 +111,8 @@ 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(["memory" in op for op in operands]):
|
||||
|
||||
if isinstance(operands, MemoryOperand) and operands.name == "memory":
|
||||
operands_reg = self.substitute_mem_address(instruction_form.operands)
|
||||
isa_data_reg = self._isa_model.get_instruction(
|
||||
instruction_form.instruction, operands_reg
|
||||
@@ -181,13 +183,13 @@ class ISASemantics(object):
|
||||
# store operand list in dict and reassign operand key/value pair
|
||||
instruction_form.semantic_operands = AttrDict.convert_dict(op_dict)
|
||||
# assign LD/ST flags
|
||||
instruction_form["flags"] = (
|
||||
instruction_form["flags"] if "flags" in instruction_form else []
|
||||
instruction_form.flags = (
|
||||
instruction_form.flags if instruction_form.flags != [] else []
|
||||
)
|
||||
if self._has_load(instruction_form):
|
||||
instruction_form["flags"] += [INSTR_FLAGS.HAS_LD]
|
||||
instruction_form.flags += [INSTR_FLAGS.HAS_LD]
|
||||
if self._has_store(instruction_form):
|
||||
instruction_form["flags"] += [INSTR_FLAGS.HAS_ST]
|
||||
instruction_form.flags += [INSTR_FLAGS.HAS_ST]
|
||||
|
||||
def get_reg_changes(self, instruction_form, only_postindexed=False):
|
||||
"""
|
||||
@@ -341,7 +343,7 @@ class ISASemantics(object):
|
||||
instruction_form.semantic_operands.source,
|
||||
instruction_form.semantic_operands.src_dst,
|
||||
):
|
||||
if "memory" in operand:
|
||||
if operand.name == "memory":
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
29
osaca/semantics/kernel_dg.py
Executable file → Normal file
29
osaca/semantics/kernel_dg.py
Executable file → Normal file
@@ -59,38 +59,37 @@ class KernelDG(nx.DiGraph):
|
||||
# 3. get LT value and set as edge weight
|
||||
dg = nx.DiGraph()
|
||||
for i, instruction_form in enumerate(kernel):
|
||||
dg.add_node(instruction_form["line_number"])
|
||||
dg.nodes[instruction_form["line_number"]]["instruction_form"] = instruction_form
|
||||
dg.add_node(instruction_form.line_number)
|
||||
print(dg.nodes[instruction_form.line_number])
|
||||
dg.nodes[instruction_form.line_number].instruction_form = instruction_form
|
||||
# add load as separate node if existent
|
||||
if (
|
||||
INSTR_FLAGS.HAS_LD in instruction_form["flags"]
|
||||
and INSTR_FLAGS.LD not in instruction_form["flags"]
|
||||
INSTR_FLAGS.HAS_LD in instruction_form.flags
|
||||
and INSTR_FLAGS.LD not in instruction_form.flags
|
||||
):
|
||||
# add new node
|
||||
dg.add_node(instruction_form["line_number"] + 0.1)
|
||||
dg.nodes[instruction_form["line_number"] + 0.1][
|
||||
"instruction_form"
|
||||
] = instruction_form
|
||||
dg.add_node(instruction_form.line_number + 0.1)
|
||||
dg.nodes[instruction_form.line_number + 0.1].instruction_form = instruction_form
|
||||
# and set LD latency as edge weight
|
||||
dg.add_edge(
|
||||
instruction_form["line_number"] + 0.1,
|
||||
instruction_form["line_number"],
|
||||
latency=instruction_form["latency"] - instruction_form["latency_wo_load"],
|
||||
instruction_form.line_number + 0.1,
|
||||
instruction_form.line_number,
|
||||
latency=instruction_form.latency - instruction_form.latency_wo_load,
|
||||
)
|
||||
for dep, dep_flags in self.find_depending(
|
||||
instruction_form, kernel[i + 1 :], flag_dependencies
|
||||
):
|
||||
edge_weight = (
|
||||
instruction_form["latency"]
|
||||
if "mem_dep" in dep_flags or "latency_wo_load" not in instruction_form
|
||||
else instruction_form["latency_wo_load"]
|
||||
instruction_form.latency
|
||||
if "mem_dep" in dep_flags or instruction_form.latency_wo_load == None
|
||||
else instruction_form.latency_wo_load
|
||||
)
|
||||
if "storeload_dep" in dep_flags and self.model is not None:
|
||||
edge_weight += self.model.get("store_to_load_forward_latency", 0)
|
||||
if "p_indexed" in dep_flags and self.model is not None:
|
||||
edge_weight = self.model.get("p_index_latency", 1)
|
||||
dg.add_edge(
|
||||
instruction_form["line_number"],
|
||||
instruction_form.line_number,
|
||||
dep["line_number"],
|
||||
latency=edge_weight,
|
||||
)
|
||||
|
||||
27
osaca/semantics/marker_utils.py
Executable file → Normal file
27
osaca/semantics/marker_utils.py
Executable file → Normal file
@@ -146,11 +146,12 @@ def find_marked_section(
|
||||
source = line.operands[0 if not reverse else 1]
|
||||
destination = line.operands[1 if not reverse else 0]
|
||||
# instruction pair matches, check for operands
|
||||
print(source)
|
||||
if (
|
||||
"immediate" in source
|
||||
and parser.normalize_imd(source.immediate) == mov_vals[0]
|
||||
and "register" in destination
|
||||
and parser.get_full_reg_name(destination.register) == mov_reg
|
||||
and parser.get_full_reg_name(destination['register']) == mov_reg
|
||||
):
|
||||
# operands of first instruction match start, check for second one
|
||||
match, line_count = match_bytes(lines, i + 1, nop_bytes)
|
||||
@@ -161,7 +162,7 @@ def find_marked_section(
|
||||
"immediate" in source
|
||||
and parser.normalize_imd(source.immediate) == mov_vals[1]
|
||||
and "register" in destination
|
||||
and parser.get_full_reg_name(destination.register) == mov_reg
|
||||
and parser.get_full_reg_name(destination['register']) == mov_reg
|
||||
):
|
||||
# operand of first instruction match end, check for second one
|
||||
match, line_count = match_bytes(lines, i + 1, nop_bytes)
|
||||
@@ -203,14 +204,14 @@ def find_jump_labels(lines):
|
||||
labels = OrderedDict()
|
||||
current_label = None
|
||||
for i, line in enumerate(lines):
|
||||
if line["label"] is not None:
|
||||
if line.label is not None:
|
||||
# When a new label is found, add to blocks dict
|
||||
labels[line["label"]] = (i,)
|
||||
labels[line.label] = (i,)
|
||||
# End previous block at previous line
|
||||
if current_label is not None:
|
||||
labels[current_label] = (labels[current_label][0], i)
|
||||
# Update current block name
|
||||
current_label = line["label"]
|
||||
current_label = line.label
|
||||
elif current_label is None:
|
||||
# If no block has been started, skip end detection
|
||||
continue
|
||||
@@ -222,9 +223,9 @@ def find_jump_labels(lines):
|
||||
for label in list(labels):
|
||||
if all(
|
||||
[
|
||||
line["instruction"].startswith(".")
|
||||
line.instruction.startswith(".")
|
||||
for line in lines[labels[label][0] : labels[label][1]]
|
||||
if line["instruction"] is not None
|
||||
if line.instruction is not None
|
||||
]
|
||||
):
|
||||
del labels[label]
|
||||
@@ -251,11 +252,11 @@ def find_basic_blocks(lines):
|
||||
terminate = False
|
||||
blocks[label].append(line)
|
||||
# Find end of block by searching for references to valid jump labels
|
||||
if line["instruction"] and line["operands"]:
|
||||
for operand in [o for o in line["operands"] if "identifier" in o]:
|
||||
if line.instruction and line.operands:
|
||||
for operand in [o for o in line.operands if "identifier" in o]:
|
||||
if operand["identifier"]["name"] in valid_jump_labels:
|
||||
terminate = True
|
||||
elif line["label"] is not None:
|
||||
elif line.label is not None:
|
||||
terminate = True
|
||||
if terminate:
|
||||
break
|
||||
@@ -280,13 +281,13 @@ def find_basic_loop_bodies(lines):
|
||||
terminate = False
|
||||
current_block.append(line)
|
||||
# Find end of block by searching for references to valid jump labels
|
||||
if line["instruction"] and line["operands"]:
|
||||
if line.instruction and line.operands:
|
||||
# Ignore `b.none` instructions (relevant von ARM SVE code)
|
||||
# This branch instruction is often present _within_ inner loop blocks, but usually
|
||||
# do not terminate
|
||||
if line["instruction"] == "b.none":
|
||||
if line.instruction == "b.none":
|
||||
continue
|
||||
for operand in [o for o in line["operands"] if "identifier" in o]:
|
||||
for operand in [o for o in line.operands if "identifier" in o]:
|
||||
if operand["identifier"]["name"] in valid_jump_labels:
|
||||
if operand["identifier"]["name"] == label:
|
||||
loop_bodies[label] = current_block
|
||||
|
||||
@@ -44,8 +44,8 @@ class TestBaseParser(unittest.TestCase):
|
||||
self.parser.parse_instruction(instr1)
|
||||
|
||||
def test_register_funcs(self):
|
||||
reg_a1 = AttrDict({"name": "rax"})
|
||||
reg_a2 = AttrDict({"name": "eax"})
|
||||
reg_a1 = {"name": "rax"}
|
||||
reg_a2 = {"name": "eax"}
|
||||
register_string = "v1.2d"
|
||||
with self.assertRaises(NotImplementedError):
|
||||
self.parser.is_reg_dependend_of(reg_a1, reg_a2)
|
||||
|
||||
26
tests/test_files/kernel_aarch64.s.copy.s
Normal file
26
tests/test_files/kernel_aarch64.s.copy.s
Normal file
@@ -0,0 +1,26 @@
|
||||
// OSACA-BEGIN
|
||||
.LBB0_32:
|
||||
ldp q4, q5, [x9, #-32]
|
||||
ldp q6, q7, [x9], #64
|
||||
ldp q16, q17, [x11, #-32]!
|
||||
ldp q18, q19, [x11], #64
|
||||
fmul v4.2d, v4.2d, v16.2d
|
||||
fmul v5.2d, v5.2d, v17.2d
|
||||
fmul v6.2d, v6.2d, v18.2d
|
||||
fmul v7.2d, v7.2d, v19.2d
|
||||
ldp q0, q1, [x8, #-32]
|
||||
ldp q2, q3, [x8], #64
|
||||
fadd v0.2d, v0.2d, v4.2d
|
||||
fadd v1.2d, v1.2d, v5.2d
|
||||
stp q0, q1, [x10, #-32]
|
||||
fadd v2.2d, v2.2d, v6.2d
|
||||
fadd v3.2d, v3.2d, v7.2d
|
||||
stp q2, q3, [x10]
|
||||
add x10, x10, #64 // =64
|
||||
adds x12, x12, #1 // =1
|
||||
fmov s0, -1.0e+0
|
||||
b.ne .LBB0_32
|
||||
// OSACA-END
|
||||
fmov s1, #2.0e+2f
|
||||
prfm pldl1keep, [x26, #2112]
|
||||
add x11, x11, x11
|
||||
@@ -458,9 +458,7 @@ class TestParserAArch64(unittest.TestCase):
|
||||
)
|
||||
|
||||
def _get_label(self, parser, label):
|
||||
return AttrDict.convert_dict(
|
||||
parser.process_operand(parser.label.parseString(label, parseAll=True).asDict())
|
||||
).label
|
||||
return parser.process_operand(parser.label.parseString(label, parseAll=True).asDict())
|
||||
|
||||
def _get_directive(self, parser, directive):
|
||||
return AttrDict.convert_dict(
|
||||
|
||||
@@ -8,7 +8,7 @@ import unittest
|
||||
|
||||
from pyparsing import ParseException
|
||||
|
||||
from osaca.parser import AttrDict, ParserX86ATT, InstructionForm
|
||||
from osaca.parser import ParserX86ATT, InstructionForm
|
||||
|
||||
class TestParserX86ATT(unittest.TestCase):
|
||||
@classmethod
|
||||
@@ -304,10 +304,8 @@ class TestParserX86ATT(unittest.TestCase):
|
||||
##################
|
||||
def _get_comment(self, parser, comment):
|
||||
return " ".join(
|
||||
AttrDict.convert_dict(
|
||||
parser.process_operand(parser.comment.parseString(comment, parseAll=True).asDict())
|
||||
).comment
|
||||
)
|
||||
parser.process_operand(parser.comment.parseString(comment, parseAll=True).asDict())['comment']
|
||||
)
|
||||
|
||||
def _get_label(self, parser, label):
|
||||
return parser.process_operand(parser.label.parseString(label, parseAll=True).asDict())
|
||||
|
||||
Reference in New Issue
Block a user