mirror of
https://github.com/RRZE-HPC/OSACA.git
synced 2026-01-06 11:10:06 +01:00
Validating of OSACA predictions for IVB, SKX, ZEN1, ZEN2, A64FX and TX2 with different kernels. build_and_run.py contains the configuration used at RRZE's testcluster and UR's qpace4, Analysis.ipynb contains the analysis script and results. Raw data from measurements (122MB) will be attached to next OSACA release. For now, find the raw data here: https://hawo.net/~sijuhamm/d/UPIhBOtz/validation-data.tar.gz The analysis report can be viewed at https://nbviewer.jupyter.org/github/RRZE-HPC/OSACA/blob/validation/validation/Analysis.ipynb Quite a few changes on OSACA included: Feature: register change tracking via semantic understanding of operations Feature: recording LCD latency along path and exposing this to frontend Feature: support for memory reference aliases Feature: store throughput scaling (similar to load throughput scaling) Fix: model importer works with latest uops.info export Fix: immediate type tracking on ARM now preserves type in internal representaion Removed unused KerncraftAPI
433 lines
18 KiB
Python
Executable File
433 lines
18 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Unit tests for ARMv8 AArch64 assembly parser
|
|
"""
|
|
|
|
import os
|
|
import unittest
|
|
|
|
from pyparsing import ParseException
|
|
|
|
from osaca.parser import AttrDict, ParserAArch64
|
|
|
|
|
|
class TestParserAArch64(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(self):
|
|
self.parser = ParserAArch64()
|
|
with open(self._find_file("triad_arm_iaca.s")) as f:
|
|
self.triad_code = f.read()
|
|
|
|
##################
|
|
# Test
|
|
##################
|
|
|
|
def test_comment_parser(self):
|
|
self.assertEqual(self._get_comment(self.parser, "// some comments"), "some comments")
|
|
self.assertEqual(self._get_comment(self.parser, "\t\t//AA BB CC \t end \t"), "AA BB CC end")
|
|
self.assertEqual(
|
|
self._get_comment(self.parser, "\t//// comment //// comment"),
|
|
"// comment //// comment",
|
|
)
|
|
|
|
def test_label_parser(self):
|
|
self.assertEqual(self._get_label(self.parser, "main:").name, "main")
|
|
self.assertEqual(self._get_label(self.parser, "..B1.10:").name, "..B1.10")
|
|
self.assertEqual(self._get_label(self.parser, ".2.3_2_pack.3:").name, ".2.3_2_pack.3")
|
|
self.assertEqual(self._get_label(self.parser, ".L1:\t\t\t//label1").name, ".L1")
|
|
self.assertEqual(
|
|
" ".join(self._get_label(self.parser, ".L1:\t\t\t//label1").comment), "label1"
|
|
)
|
|
with self.assertRaises(ParseException):
|
|
self._get_label(self.parser, "\t.cfi_startproc")
|
|
|
|
def test_directive_parser(self):
|
|
self.assertEqual(self._get_directive(self.parser, "\t.text").name, "text")
|
|
self.assertEqual(len(self._get_directive(self.parser, "\t.text").parameters), 0)
|
|
self.assertEqual(self._get_directive(self.parser, "\t.align\t16,0x90").name, "align")
|
|
self.assertEqual(len(self._get_directive(self.parser, "\t.align\t16,0x90").parameters), 2)
|
|
self.assertEqual(
|
|
self._get_directive(self.parser, "\t.align\t16,0x90").parameters[1], "0x90"
|
|
)
|
|
self.assertEqual(
|
|
self._get_directive(self.parser, " .byte 100,103,144 //IACA START")[
|
|
"name"
|
|
],
|
|
"byte",
|
|
)
|
|
self.assertEqual(
|
|
self._get_directive(self.parser, " .byte 100,103,144 //IACA START")[
|
|
"parameters"
|
|
][2],
|
|
"144",
|
|
)
|
|
self.assertEqual(
|
|
" ".join(
|
|
self._get_directive(self.parser, " .byte 100,103,144 //IACA START")[
|
|
"comment"
|
|
]
|
|
),
|
|
"IACA START",
|
|
)
|
|
|
|
def test_parse_instruction(self):
|
|
instr1 = "\t\tvcvt.F32.S32 w1, w2\t\t\t//12.27"
|
|
instr2 = "b.lo ..B1.4 \t"
|
|
instr3 = " mov x2,#0x222 //NOT IACA END"
|
|
instr4 = "str x28, [sp, x1, lsl #4] //12.9"
|
|
instr5 = "ldr x0, [x0, #:got_lo12:q2c]"
|
|
instr6 = "adrp x0, :got:visited"
|
|
instr7 = "fadd v17.2d, v16.2d, v1.2d"
|
|
|
|
parsed_1 = self.parser.parse_instruction(instr1)
|
|
parsed_2 = self.parser.parse_instruction(instr2)
|
|
parsed_3 = self.parser.parse_instruction(instr3)
|
|
parsed_4 = self.parser.parse_instruction(instr4)
|
|
parsed_5 = self.parser.parse_instruction(instr5)
|
|
parsed_6 = self.parser.parse_instruction(instr6)
|
|
parsed_7 = self.parser.parse_instruction(instr7)
|
|
|
|
self.assertEqual(parsed_1.instruction, "vcvt.F32.S32")
|
|
self.assertEqual(parsed_1.operands[0].register.name, "1")
|
|
self.assertEqual(parsed_1.operands[0].register.prefix, "w")
|
|
self.assertEqual(parsed_1.operands[1].register.name, "2")
|
|
self.assertEqual(parsed_1.operands[1].register.prefix, "w")
|
|
self.assertEqual(parsed_1.comment, "12.27")
|
|
|
|
self.assertEqual(parsed_2.instruction, "b.lo")
|
|
self.assertEqual(parsed_2.operands[0].identifier.name, "..B1.4")
|
|
self.assertEqual(len(parsed_2.operands), 1)
|
|
self.assertIsNone(parsed_2.comment)
|
|
|
|
self.assertEqual(parsed_3.instruction, "mov")
|
|
self.assertEqual(parsed_3.operands[0].register.name, "2")
|
|
self.assertEqual(parsed_3.operands[0].register.prefix, "x")
|
|
self.assertEqual(parsed_3.operands[1].immediate.value, "0x222")
|
|
self.assertEqual(parsed_3.comment, "NOT IACA END")
|
|
|
|
self.assertEqual(parsed_4.instruction, "str")
|
|
self.assertIsNone(parsed_4.operands[1].memory.offset)
|
|
self.assertEqual(parsed_4.operands[1].memory.base.name, "sp")
|
|
self.assertEqual(parsed_4.operands[1].memory.base.prefix, "x")
|
|
self.assertEqual(parsed_4.operands[1].memory.index.name, "1")
|
|
self.assertEqual(parsed_4.operands[1].memory.index.prefix, "x")
|
|
self.assertEqual(parsed_4.operands[1].memory.scale, 16)
|
|
self.assertEqual(parsed_4.operands[0].register.name, "28")
|
|
self.assertEqual(parsed_4.operands[0].register.prefix, "x")
|
|
self.assertEqual(parsed_4.comment, "12.9")
|
|
|
|
self.assertEqual(parsed_5.instruction, "ldr")
|
|
self.assertEqual(parsed_5.operands[0].register.name, "0")
|
|
self.assertEqual(parsed_5.operands[0].register.prefix, "x")
|
|
self.assertEqual(parsed_5.operands[1].memory.offset.identifier.name, "q2c")
|
|
self.assertEqual(parsed_5.operands[1].memory.offset.identifier.relocation, ":got_lo12:")
|
|
self.assertEqual(parsed_5.operands[1].memory.base.name, "0")
|
|
self.assertEqual(parsed_5.operands[1].memory.base.prefix, "x")
|
|
self.assertIsNone(parsed_5.operands[1].memory.index)
|
|
self.assertEqual(parsed_5.operands[1].memory.scale, 1)
|
|
|
|
self.assertEqual(parsed_6.instruction, "adrp")
|
|
self.assertEqual(parsed_6.operands[0].register.name, "0")
|
|
self.assertEqual(parsed_6.operands[0].register.prefix, "x")
|
|
self.assertEqual(parsed_6.operands[1].identifier.relocation, ":got:")
|
|
self.assertEqual(parsed_6.operands[1].identifier.name, "visited")
|
|
|
|
self.assertEqual(parsed_7.instruction, "fadd")
|
|
self.assertEqual(parsed_7.operands[0].register.name, "17")
|
|
self.assertEqual(parsed_7.operands[0].register.prefix, "v")
|
|
self.assertEqual(parsed_7.operands[0].register.lanes, "2")
|
|
self.assertEqual(parsed_7.operands[0].register.shape, "d")
|
|
self.assertEqual(self.parser.get_full_reg_name(parsed_7.operands[2].register), "v1.2d")
|
|
|
|
def test_parse_line(self):
|
|
line_comment = "// -- Begin main"
|
|
line_label = ".LBB0_1: // =>This Inner Loop Header: Depth=1"
|
|
line_directive = ".cfi_def_cfa w29, -16"
|
|
line_instruction = "ldr s0, [x11, w10, sxtw #2] // = <<2"
|
|
line_prefetch = "prfm pldl1keep, [x26, #2048] //HPL"
|
|
line_preindexed = "stp x29, x30, [sp, #-16]!"
|
|
line_postindexed = "ldp q2, q3, [x11], #64"
|
|
line_5_operands = "fcmla z26.d, p0/m, z29.d, z21.d, #90"
|
|
|
|
instruction_form_1 = {
|
|
"instruction": None,
|
|
"operands": [],
|
|
"directive": None,
|
|
"comment": "-- Begin main",
|
|
"label": None,
|
|
"line": "// -- Begin main",
|
|
"line_number": 1,
|
|
}
|
|
|
|
instruction_form_2 = {
|
|
"instruction": None,
|
|
"operands": [],
|
|
"directive": None,
|
|
"comment": "=>This Inner Loop Header: Depth=1",
|
|
"label": ".LBB0_1",
|
|
"line": ".LBB0_1: // =>This Inner Loop Header: Depth=1",
|
|
"line_number": 2,
|
|
}
|
|
instruction_form_3 = {
|
|
"instruction": None,
|
|
"operands": [],
|
|
"directive": {"name": "cfi_def_cfa", "parameters": ["w29", "-16"]},
|
|
"comment": None,
|
|
"label": None,
|
|
"line": ".cfi_def_cfa w29, -16",
|
|
"line_number": 3,
|
|
}
|
|
instruction_form_4 = {
|
|
"instruction": "ldr",
|
|
"operands": [
|
|
{"register": {"prefix": "s", "name": "0"}},
|
|
{
|
|
"memory": {
|
|
"offset": None,
|
|
"base": {"prefix": "x", "name": "11"},
|
|
"index": {
|
|
"prefix": "w",
|
|
"name": "10",
|
|
"shift_op": "sxtw",
|
|
"immediate": {"value": "2"},
|
|
"shift": [{"value": "2"}],
|
|
},
|
|
"scale": 4,
|
|
}
|
|
},
|
|
],
|
|
"directive": None,
|
|
"comment": "= <<2",
|
|
"label": None,
|
|
"line": "ldr s0, [x11, w10, sxtw #2] // = <<2",
|
|
"line_number": 4,
|
|
}
|
|
instruction_form_5 = {
|
|
"instruction": "prfm",
|
|
"operands": [
|
|
{"prfop": {"type": ["PLD"], "target": ["L1"], "policy": ["KEEP"]}},
|
|
{
|
|
"memory": {
|
|
"offset": {"value": "2048"},
|
|
"base": {"prefix": "x", "name": "26"},
|
|
"index": None,
|
|
"scale": 1,
|
|
}
|
|
},
|
|
],
|
|
"directive": None,
|
|
"comment": "HPL",
|
|
"label": None,
|
|
"line": "prfm pldl1keep, [x26, #2048] //HPL",
|
|
"line_number": 5,
|
|
}
|
|
instruction_form_6 = {
|
|
"instruction": "stp",
|
|
"operands": [
|
|
{"register": {"prefix": "x", "name": "29"}},
|
|
{"register": {"prefix": "x", "name": "30"}},
|
|
{
|
|
"memory": {
|
|
"offset": {"value": "-16"},
|
|
"base": {"name": "sp", "prefix": "x"},
|
|
"index": None,
|
|
"scale": 1,
|
|
"pre_indexed": True,
|
|
}
|
|
},
|
|
],
|
|
"directive": None,
|
|
"comment": None,
|
|
"label": None,
|
|
"line": "stp x29, x30, [sp, #-16]!",
|
|
"line_number": 6,
|
|
}
|
|
instruction_form_7 = {
|
|
"instruction": "ldp",
|
|
"operands": [
|
|
{"register": {"prefix": "q", "name": "2"}},
|
|
{"register": {"prefix": "q", "name": "3"}},
|
|
{
|
|
"memory": {
|
|
"offset": None,
|
|
"base": {"prefix": "x", "name": "11"},
|
|
"index": None,
|
|
"scale": 1,
|
|
"post_indexed": {"value": "64"},
|
|
}
|
|
},
|
|
],
|
|
"directive": None,
|
|
"comment": None,
|
|
"label": None,
|
|
"line": "ldp q2, q3, [x11], #64",
|
|
"line_number": 7,
|
|
}
|
|
instruction_form_8 = {
|
|
"instruction": "fcmla",
|
|
"operands": [
|
|
{"register": {"prefix": "z", "name": "26", "shape": "d"}},
|
|
{"register": {"prefix": "p", "name": "0", "predication": "m"}},
|
|
{"register": {"prefix": "z", "name": "29", "shape": "d"}},
|
|
{"register": {"prefix": "z", "name": "21", "shape": "d"}},
|
|
{"immediate": {"value": "90", "type": "int"}},
|
|
],
|
|
"directive": None,
|
|
"comment": None,
|
|
"label": None,
|
|
"line": "fcmla z26.d, p0/m, z29.d, z21.d, #90",
|
|
"line_number": 8,
|
|
}
|
|
|
|
parsed_1 = self.parser.parse_line(line_comment, 1)
|
|
parsed_2 = self.parser.parse_line(line_label, 2)
|
|
parsed_3 = self.parser.parse_line(line_directive, 3)
|
|
parsed_4 = self.parser.parse_line(line_instruction, 4)
|
|
parsed_5 = self.parser.parse_line(line_prefetch, 5)
|
|
parsed_6 = self.parser.parse_line(line_preindexed, 6)
|
|
parsed_7 = self.parser.parse_line(line_postindexed, 7)
|
|
parsed_8 = self.parser.parse_line(line_5_operands, 8)
|
|
|
|
self.assertEqual(parsed_1, instruction_form_1)
|
|
self.assertEqual(parsed_2, instruction_form_2)
|
|
self.assertEqual(parsed_3, instruction_form_3)
|
|
self.assertEqual(parsed_4, instruction_form_4)
|
|
self.assertEqual(parsed_5, instruction_form_5)
|
|
self.assertEqual(parsed_6, instruction_form_6)
|
|
self.assertEqual(parsed_7, instruction_form_7)
|
|
self.assertEqual(parsed_8, instruction_form_8)
|
|
|
|
def test_parse_file(self):
|
|
parsed = self.parser.parse_file(self.triad_code)
|
|
self.assertEqual(parsed[0].line_number, 1)
|
|
self.assertEqual(len(parsed), 645)
|
|
|
|
def test_normalize_imd(self):
|
|
imd_decimal_1 = {"value": "79"}
|
|
imd_hex_1 = {"value": "0x4f"}
|
|
imd_decimal_2 = {"value": "8"}
|
|
imd_hex_2 = {"value": "0x8"}
|
|
imd_float_11 = {"float": {"mantissa": "0.79", "e_sign": "+", "exponent": "2"}}
|
|
imd_float_12 = {"float": {"mantissa": "790.0", "e_sign": "-", "exponent": "1"}}
|
|
imd_double_11 = {"double": {"mantissa": "0.79", "e_sign": "+", "exponent": "2"}}
|
|
imd_double_12 = {"double": {"mantissa": "790.0", "e_sign": "-", "exponent": "1"}}
|
|
identifier = {"identifier": {"name": "..B1.4"}}
|
|
|
|
value1 = self.parser.normalize_imd(imd_decimal_1)
|
|
self.assertEqual(value1, self.parser.normalize_imd(imd_hex_1))
|
|
self.assertEqual(
|
|
self.parser.normalize_imd(imd_decimal_2), self.parser.normalize_imd(imd_hex_2)
|
|
)
|
|
self.assertEqual(self.parser.normalize_imd(imd_float_11), value1)
|
|
self.assertEqual(self.parser.normalize_imd(imd_float_12), value1)
|
|
self.assertEqual(self.parser.normalize_imd(imd_double_11), value1)
|
|
self.assertEqual(self.parser.normalize_imd(imd_double_12), value1)
|
|
self.assertEqual(self.parser.normalize_imd(identifier), identifier)
|
|
|
|
def test_multiple_regs(self):
|
|
instr_range = "PUSH {x5-x7}"
|
|
reg_range = AttrDict(
|
|
{
|
|
"register": {
|
|
"range": [{"prefix": "x", "name": "5"}, {"prefix": "x", "name": "7"}],
|
|
"index": None,
|
|
}
|
|
}
|
|
)
|
|
instr_list = "POP {x5, x7, x9}"
|
|
reg_list = AttrDict(
|
|
{
|
|
"register": {
|
|
"list": [
|
|
{"prefix": "x", "name": "5"},
|
|
{"prefix": "x", "name": "7"},
|
|
{"prefix": "x", "name": "9"},
|
|
],
|
|
"index": None,
|
|
}
|
|
}
|
|
)
|
|
prange = self.parser.parse_line(instr_range)
|
|
plist = self.parser.parse_line(instr_list)
|
|
|
|
self.assertEqual(prange.operands[0], reg_range)
|
|
self.assertEqual(plist.operands[0], reg_list)
|
|
|
|
def test_reg_dependency(self):
|
|
reg_1_1 = AttrDict({"prefix": "b", "name": "1"})
|
|
reg_1_2 = AttrDict({"prefix": "h", "name": "1"})
|
|
reg_1_3 = AttrDict({"prefix": "s", "name": "1"})
|
|
reg_1_4 = AttrDict({"prefix": "d", "name": "1"})
|
|
reg_1_4 = AttrDict({"prefix": "q", "name": "1"})
|
|
reg_2_1 = AttrDict({"prefix": "w", "name": "2"})
|
|
reg_2_2 = AttrDict({"prefix": "x", "name": "2"})
|
|
reg_v1_1 = AttrDict({"prefix": "v", "name": "11", "lanes": "16", "shape": "b"})
|
|
reg_v1_2 = AttrDict({"prefix": "v", "name": "11", "lanes": "8", "shape": "h"})
|
|
reg_v1_3 = AttrDict({"prefix": "v", "name": "11", "lanes": "4", "shape": "s"})
|
|
reg_v1_4 = AttrDict({"prefix": "v", "name": "11", "lanes": "2", "shape": "d"})
|
|
|
|
reg_b5 = AttrDict({"prefix": "b", "name": "5"})
|
|
reg_q15 = AttrDict({"prefix": "q", "name": "15"})
|
|
reg_v10 = AttrDict({"prefix": "v", "name": "10", "lanes": "2", "shape": "s"})
|
|
reg_v20 = AttrDict({"prefix": "v", "name": "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]
|
|
reg_v = [reg_v1_1, reg_v1_2, reg_v1_3, reg_v1_4]
|
|
reg_others = [reg_b5, reg_q15, reg_v10, reg_v20]
|
|
regs = reg_1 + reg_2 + reg_v + reg_others
|
|
|
|
# test each register against each other
|
|
for ri in reg_1:
|
|
for rj in regs:
|
|
assert_value = True if rj in reg_1 else False
|
|
with self.subTest(reg_a=ri, reg_b=rj, assert_val=assert_value):
|
|
self.assertEqual(self.parser.is_reg_dependend_of(ri, rj), assert_value)
|
|
for ri in reg_2:
|
|
for rj in regs:
|
|
assert_value = True if rj in reg_2 else False
|
|
with self.subTest(reg_a=ri, reg_b=rj, assert_val=assert_value):
|
|
self.assertEqual(self.parser.is_reg_dependend_of(ri, rj), assert_value)
|
|
for ri in reg_v:
|
|
for rj in regs:
|
|
assert_value = True if rj in reg_v else False
|
|
with self.subTest(reg_a=ri, reg_b=rj, assert_val=assert_value):
|
|
self.assertEqual(self.parser.is_reg_dependend_of(ri, rj), assert_value)
|
|
for ri in reg_others:
|
|
for rj in regs:
|
|
assert_value = True if rj == ri else False
|
|
with self.subTest(reg_a=ri, reg_b=rj, assert_val=assert_value):
|
|
self.assertEqual(self.parser.is_reg_dependend_of(ri, rj), assert_value)
|
|
|
|
##################
|
|
# Helper functions
|
|
##################
|
|
def _get_comment(self, parser, comment):
|
|
return " ".join(
|
|
AttrDict.convert_dict(
|
|
parser.process_operand(parser.comment.parseString(comment, parseAll=True).asDict())
|
|
).comment
|
|
)
|
|
|
|
def _get_label(self, parser, label):
|
|
return AttrDict.convert_dict(
|
|
parser.process_operand(parser.label.parseString(label, parseAll=True).asDict())
|
|
).label
|
|
|
|
def _get_directive(self, parser, directive):
|
|
return AttrDict.convert_dict(
|
|
parser.process_operand(parser.directive.parseString(directive, parseAll=True).asDict())
|
|
).directive
|
|
|
|
@staticmethod
|
|
def _find_file(name):
|
|
testdir = os.path.dirname(__file__)
|
|
name = os.path.join(testdir, "test_files", name)
|
|
assert os.path.exists(name)
|
|
return name
|
|
|
|
|
|
if __name__ == "__main__":
|
|
suite = unittest.TestLoader().loadTestsFromTestCase(TestParserAArch64)
|
|
unittest.TextTestRunner(verbosity=2).run(suite)
|