Files
OSACA/tests/test_parser_AArch64.py
Julian 08440ed5e1 Validation (#71)
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
2021-04-15 14:42:37 +02:00

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)