mirror of
https://github.com/RRZE-HPC/OSACA.git
synced 2025-12-13 07:30:06 +01:00
470 lines
18 KiB
Python
Executable File
470 lines
18 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Unit tests for IACA/OSACA marker utilities
|
|
"""
|
|
import os
|
|
import unittest
|
|
from collections import OrderedDict
|
|
|
|
from osaca.semantics import (
|
|
reduce_to_section,
|
|
find_basic_blocks,
|
|
find_jump_labels,
|
|
find_basic_loop_bodies,
|
|
)
|
|
from osaca.parser import ParserAArch64, ParserX86ATT, ParserX86Intel
|
|
|
|
|
|
class TestMarkerUtils(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(self):
|
|
self.parser_AArch = ParserAArch64()
|
|
self.parser_x86_att = ParserX86ATT()
|
|
self.parser_x86_intel = ParserX86Intel()
|
|
with open(self._find_file("triad_arm_iaca.s")) as f:
|
|
triad_code_arm = f.read()
|
|
with open(self._find_file("triad_x86_iaca.s")) as f:
|
|
triad_code_x86_att = f.read()
|
|
with open(self._find_file("triad_x86_intel_iaca.s")) as f:
|
|
triad_code_x86_intel = f.read()
|
|
self.parsed_AArch = self.parser_AArch.parse_file(triad_code_arm)
|
|
self.parsed_x86_att = self.parser_x86_att.parse_file(triad_code_x86_att)
|
|
self.parsed_x86_intel = self.parser_x86_intel.parse_file(triad_code_x86_intel)
|
|
|
|
#################
|
|
# Test
|
|
#################
|
|
|
|
def test_marker_detection_AArch64(self):
|
|
kernel = reduce_to_section(self.parsed_AArch, ParserAArch64())
|
|
self.assertEqual(len(kernel), 138)
|
|
self.assertEqual(kernel[0].line_number, 307)
|
|
self.assertEqual(kernel[-1].line_number, 444)
|
|
|
|
def test_marker_detection_x86_att(self):
|
|
kernel = reduce_to_section(self.parsed_x86_att, ParserX86ATT())
|
|
self.assertEqual(len(kernel), 9)
|
|
self.assertEqual(kernel[0].line_number, 146)
|
|
self.assertEqual(kernel[-1].line_number, 154)
|
|
|
|
def test_marker_detection_x86_intel(self):
|
|
kernel = reduce_to_section(self.parsed_x86_intel, ParserX86Intel())
|
|
self.assertEqual(len(kernel), 7)
|
|
self.assertEqual(kernel[0].line_number, 111)
|
|
self.assertEqual(kernel[-1].line_number, 117)
|
|
|
|
def test_marker_matching_AArch64(self):
|
|
# preparation
|
|
bytes_1_line = ".byte 213,3,32,31\n"
|
|
bytes_2_lines_1 = ".byte 213,3,32\n" + ".byte 31\n"
|
|
bytes_2_lines_2 = ".byte 213,3\n" + ".byte 32,31\n"
|
|
bytes_2_lines_3 = ".byte 213\n" + ".byte 3,32,31\n"
|
|
bytes_3_lines_1 = ".byte 213,3\n" + ".byte 32\n" + ".byte 31\n"
|
|
bytes_3_lines_2 = ".byte 213\n" + ".byte 3,32\n" + ".byte 31\n"
|
|
bytes_3_lines_3 = ".byte 213\n" + ".byte 3\n" + ".byte 32,31\n"
|
|
bytes_4_lines = ".byte 213\n" + ".byte 3\n" + ".byte 32\n" + ".byte 31\n"
|
|
bytes_hex = ".byte 0xd5, 0x3, 0x20, 0x1f\n"
|
|
bytes_mixed = ".byte 0xd5\n.byte 3,0x20\n.byte 31\n"
|
|
mov_start_1 = "mov x1, #111\n"
|
|
mov_start_2 = "mov x1, 111 // should work as well\n"
|
|
mov_end_1 = "mov x1, #222 // preferred way\n"
|
|
mov_end_2 = "mov x1, 222\n"
|
|
prologue = (
|
|
"mov x12, xzr\n"
|
|
+ "\tldp x9, x10, [sp, #16] // 8-byte Folded Reload\n"
|
|
+ " .p2align 6\n"
|
|
)
|
|
kernel = (
|
|
".LBB0_28:\n"
|
|
+ "fmul v7.2d, v7.2d, v19.2d\n"
|
|
+ "stp q0, q1, [x10, #-32]\n"
|
|
+ "b.ne .LBB0_28\n"
|
|
)
|
|
epilogue = ".LBB0_29: // Parent Loop BB0_20 Depth=1\n" + "bl dummy\n"
|
|
kernel_length = len(list(filter(None, kernel.split("\n"))))
|
|
|
|
bytes_variations = [
|
|
bytes_1_line,
|
|
bytes_2_lines_1,
|
|
bytes_2_lines_2,
|
|
bytes_2_lines_3,
|
|
bytes_3_lines_1,
|
|
bytes_3_lines_2,
|
|
bytes_3_lines_3,
|
|
bytes_4_lines,
|
|
bytes_hex,
|
|
bytes_mixed,
|
|
]
|
|
mov_start_variations = [mov_start_1, mov_start_2]
|
|
mov_end_variations = [mov_end_1, mov_end_2]
|
|
# actual tests
|
|
for mov_start_var in mov_start_variations:
|
|
for bytes_var_1 in bytes_variations:
|
|
for mov_end_var in mov_end_variations:
|
|
for bytes_var_2 in bytes_variations:
|
|
sample_code = (
|
|
prologue
|
|
+ mov_start_var
|
|
+ bytes_var_1
|
|
+ kernel
|
|
+ mov_end_var
|
|
+ bytes_var_2
|
|
+ epilogue
|
|
)
|
|
with self.subTest(
|
|
mov_start=mov_start_var,
|
|
bytes_start=bytes_var_1,
|
|
mov_end=mov_end_var,
|
|
bytes_end=bytes_var_2,
|
|
):
|
|
sample_parsed = self.parser_AArch.parse_file(sample_code)
|
|
sample_kernel = reduce_to_section(sample_parsed, ParserAArch64())
|
|
self.assertEqual(len(sample_kernel), kernel_length)
|
|
kernel_start = len(
|
|
list(
|
|
filter(
|
|
None,
|
|
(prologue + mov_start_var + bytes_var_1).split("\n"),
|
|
)
|
|
)
|
|
)
|
|
parsed_kernel = self.parser_AArch.parse_file(
|
|
kernel, start_line=kernel_start
|
|
)
|
|
self.assertEqual(sample_kernel, parsed_kernel)
|
|
|
|
def test_marker_matching_x86(self):
|
|
# preparation
|
|
bytes_1_line = ".byte 100,103,144\n"
|
|
bytes_2_lines_1 = ".byte 100,103\n" + ".byte 144\n"
|
|
bytes_2_lines_2 = ".byte 100\n" + ".byte 103,144\n"
|
|
bytes_3_lines = (
|
|
".byte 100 # IACA MARKER UTILITY\n"
|
|
+ ".byte 103 # IACA MARKER UTILITY\n"
|
|
+ ".byte 144 # IACA MARKER UTILITY\n"
|
|
)
|
|
bytes_hex_line = ".byte 0x64,0x67,0x90\n"
|
|
bytes_mixed = ".byte 0x64 # MARKER\n .byte 103,0x90 # ANOTHER MARKER\n"
|
|
mov_start_1 = "movl $111, %ebx # IACA START\n"
|
|
mov_start_2 = "mov $111, %ebx # IACA START\n"
|
|
mov_end_1 = "movl $222, %ebx # IACA END\n"
|
|
mov_end_2 = "mov $222, %ebx # IACA END\n"
|
|
prologue = "movl -92(%rbp), %r11d\n" + "movl $111, %ebx\n"
|
|
kernel = (
|
|
"vfmadd132sd (%r15,%rcx,8), %xmm5, %xmm0\n"
|
|
+ "vmovsd %xmm0, (%r14,%rcx,8)\n"
|
|
+ "cmpl %ebx, %ecx\n"
|
|
+ "jge .L8\n"
|
|
)
|
|
epilogue = ".LE9:\t\t#12.2\n" "call dummy\n"
|
|
kernel_length = len(list(filter(None, kernel.split("\n"))))
|
|
|
|
bytes_variations = [
|
|
bytes_1_line,
|
|
bytes_2_lines_1,
|
|
bytes_2_lines_2,
|
|
bytes_3_lines,
|
|
bytes_hex_line,
|
|
bytes_mixed,
|
|
]
|
|
mov_start_variations = [mov_start_1, mov_start_2]
|
|
mov_end_variations = [mov_end_1, mov_end_2]
|
|
# actual tests
|
|
for mov_start_var in mov_start_variations:
|
|
for bytes_var_1 in bytes_variations:
|
|
for mov_end_var in mov_end_variations:
|
|
for bytes_var_2 in bytes_variations:
|
|
sample_code = (
|
|
prologue
|
|
+ mov_start_var
|
|
+ bytes_var_1
|
|
+ kernel
|
|
+ mov_end_var
|
|
+ bytes_var_2
|
|
+ epilogue
|
|
)
|
|
with self.subTest(
|
|
mov_start=mov_start_var,
|
|
bytes_start=bytes_var_1,
|
|
mov_end=mov_end_var,
|
|
bytes_end=bytes_var_2,
|
|
):
|
|
sample_parsed = self.parser_x86_att.parse_file(sample_code)
|
|
sample_kernel = reduce_to_section(sample_parsed, ParserX86ATT())
|
|
self.assertEqual(len(sample_kernel), kernel_length)
|
|
kernel_start = len(
|
|
list(
|
|
filter(
|
|
None,
|
|
(prologue + mov_start_var + bytes_var_1).split("\n"),
|
|
)
|
|
)
|
|
)
|
|
parsed_kernel = self.parser_x86_att.parse_file(
|
|
kernel, start_line=kernel_start
|
|
)
|
|
self.assertEqual(sample_kernel, parsed_kernel)
|
|
|
|
def test_marker_special_cases_AArch(self):
|
|
bytes_line = ".byte 213,3,32,31\n"
|
|
start_marker = "mov x1, #111\n" + bytes_line
|
|
end_marker = "mov x1, #222\n" + bytes_line
|
|
prologue = "dup v0.2d, x14\n" "neg x9, x9\n" ".p2align 6\n"
|
|
kernel = (
|
|
".LBB0_28:\n"
|
|
+ "fmul v7.2d, v7.2d, v19.2d\n"
|
|
+ "stp q0, q1, [x10, #-32]\n"
|
|
+ "b.ne .LBB0_28\n"
|
|
)
|
|
epilogue = ".LBB0_29: // Parent Loop BB0_20 Depth=1\n" "bl dummy\n"
|
|
|
|
samples = [
|
|
# (test name,
|
|
# ignored prologue, section to be extraced, ignored epilogue)
|
|
("markers", prologue + start_marker, kernel, end_marker + epilogue),
|
|
("marker at file start", start_marker, kernel, end_marker + epilogue),
|
|
("no start marker", "", prologue + kernel, end_marker + epilogue),
|
|
("marker at file end", prologue + start_marker, kernel, end_marker),
|
|
("no end marker", prologue + start_marker, kernel + epilogue, ""),
|
|
("empty kernel", prologue + start_marker, "", end_marker + epilogue),
|
|
]
|
|
|
|
for test_name, pro, kernel, epi in samples:
|
|
code = pro + kernel + epi
|
|
parsed = self.parser_AArch.parse_file(code)
|
|
test_kernel = reduce_to_section(parsed, ParserAArch64())
|
|
if kernel:
|
|
kernel_length = len(kernel.strip().split("\n"))
|
|
else:
|
|
kernel_length = 0
|
|
self.assertEqual(
|
|
len(test_kernel),
|
|
kernel_length,
|
|
msg="Invalid extracted kernel length on {!r} sample".format(test_name),
|
|
)
|
|
if pro:
|
|
kernel_start = len((pro).strip().split("\n"))
|
|
else:
|
|
kernel_start = 0
|
|
parsed_kernel = self.parser_AArch.parse_file(kernel, start_line=kernel_start)
|
|
self.assertEqual(
|
|
test_kernel,
|
|
parsed_kernel,
|
|
msg="Invalid extracted kernel on {!r}".format(test_name),
|
|
)
|
|
|
|
def test_marker_special_cases_x86(self):
|
|
bytes_line = ".byte 100\n" ".byte 103\n" ".byte 144\n"
|
|
start_marker = "movl $111, %ebx\n" + bytes_line
|
|
end_marker = "movl $222, %ebx\n" + bytes_line
|
|
prologue = "movl -88(%rbp), %r10d\n" "xorl %r11d, %r11d\n" ".p2align 4,,10\n"
|
|
kernel = (
|
|
".L3: #L3\n"
|
|
"vmovsd .LC1(%rip), %xmm0\n"
|
|
"vmovsd %xmm0, (%r15,%rcx,8)\n"
|
|
"cmpl %ecx, %ebx\n"
|
|
"jle .L3\n"
|
|
)
|
|
epilogue = "leaq -56(%rbp), %rsi\n" "movl %r10d, -88(%rbp)\n" "call timing\n"
|
|
samples = [
|
|
# (test name,
|
|
# ignored prologue, section to be extraced, ignored epilogue)
|
|
("markers", prologue + start_marker, kernel, end_marker + epilogue),
|
|
("marker at file start", start_marker, kernel, end_marker + epilogue),
|
|
("no start marker", "", prologue + kernel, end_marker + epilogue),
|
|
("marker at file end", prologue + start_marker, kernel, end_marker),
|
|
("no end marker", prologue + start_marker, kernel + epilogue, ""),
|
|
("empty kernel", prologue + start_marker, "", end_marker + epilogue),
|
|
]
|
|
|
|
for test_name, pro, kernel, epi in samples:
|
|
code = pro + kernel + epi
|
|
parsed = self.parser_x86_att.parse_file(code)
|
|
test_kernel = reduce_to_section(parsed, ParserX86ATT())
|
|
if kernel:
|
|
kernel_length = len(kernel.strip().split("\n"))
|
|
else:
|
|
kernel_length = 0
|
|
self.assertEqual(
|
|
len(test_kernel),
|
|
kernel_length,
|
|
msg="Invalid extracted kernel length on {!r} sample".format(test_name),
|
|
)
|
|
if pro:
|
|
kernel_start = len((pro).strip().split("\n"))
|
|
else:
|
|
kernel_start = 0
|
|
parsed_kernel = self.parser_x86_att.parse_file(kernel, start_line=kernel_start)
|
|
|
|
self.assertEqual(
|
|
test_kernel,
|
|
parsed_kernel,
|
|
msg="Invalid extracted kernel on {!r}".format(test_name),
|
|
)
|
|
|
|
def test_find_jump_labels(self):
|
|
self.assertEqual(
|
|
find_jump_labels(self.parsed_x86_att),
|
|
OrderedDict(
|
|
[
|
|
(".LFB24", 10),
|
|
(".L4", 65),
|
|
(".L3", 79),
|
|
(".L2", 102),
|
|
(".L13", 111),
|
|
(".L12", 120),
|
|
(".L6", 132),
|
|
(".L10", 145),
|
|
(".L9", 161),
|
|
(".L8", 183),
|
|
(".L15", 252),
|
|
(".L26", 256),
|
|
(".L14", 259),
|
|
(".LFB25", 277),
|
|
(".L28", 289),
|
|
]
|
|
),
|
|
)
|
|
|
|
self.assertEqual(
|
|
find_jump_labels(self.parsed_AArch),
|
|
OrderedDict(
|
|
[
|
|
("triad", 18),
|
|
(".LBB0_3", 71),
|
|
(".LBB0_4", 76),
|
|
(".LBB0_5", 84),
|
|
(".LBB0_7", 92),
|
|
(".LBB0_8", 95),
|
|
(".LBB0_9", 106),
|
|
(".LBB0_11", 118),
|
|
(".LBB0_12", 133),
|
|
(".LBB0_14", 177),
|
|
(".LBB0_15", 190),
|
|
(".LBB0_16", 205),
|
|
(".LBB0_17", 208),
|
|
(".LBB0_18", 221),
|
|
(".LBB0_19", 228),
|
|
(".LBB0_20", 260),
|
|
(".LBB0_22", 272),
|
|
(".LBB0_24", 283),
|
|
(".LBB0_26", 290),
|
|
(".LBB0_28", 298),
|
|
(".LBB0_29", 306),
|
|
(".LBB0_31", 448),
|
|
(".LBB0_32", 458),
|
|
(".LBB0_33", 480),
|
|
(".LBB0_34", 484),
|
|
(".LBB0_35", 493),
|
|
(".LBB0_36", 504),
|
|
(".LBB0_37", 508),
|
|
(".LBB0_38", 518),
|
|
("main", 574),
|
|
]
|
|
),
|
|
)
|
|
|
|
def test_find_basic_blocks(self):
|
|
self.assertEqual(
|
|
[
|
|
(k, v[0].line_number, v[-1].line_number)
|
|
for k, v in find_basic_blocks(self.parsed_x86_att).items()
|
|
],
|
|
[
|
|
(".LFB24", 11, 56),
|
|
(".L4", 66, 74),
|
|
(".L3", 80, 89),
|
|
(".L2", 103, 112),
|
|
(".L13", 112, 121),
|
|
(".L12", 121, 125),
|
|
(".L6", 133, 135),
|
|
(".L10", 146, 154),
|
|
(".L9", 162, 170),
|
|
(".L8", 184, 187),
|
|
(".L15", 253, 256),
|
|
(".L26", 257, 259),
|
|
(".L14", 260, 262),
|
|
(".LFB25", 278, 290),
|
|
(".L28", 290, 300),
|
|
],
|
|
)
|
|
|
|
self.assertEqual(
|
|
[
|
|
(k, v[0].line_number, v[-1].line_number)
|
|
for k, v in find_basic_blocks(self.parsed_AArch).items()
|
|
],
|
|
[
|
|
("triad", 19, 64),
|
|
(".LBB0_3", 72, 77),
|
|
(".LBB0_4", 77, 83),
|
|
(".LBB0_5", 85, 89),
|
|
(".LBB0_7", 93, 95),
|
|
(".LBB0_8", 96, 105),
|
|
(".LBB0_9", 107, 114),
|
|
(".LBB0_11", 119, 134),
|
|
(".LBB0_12", 134, 173),
|
|
(".LBB0_14", 178, 191),
|
|
(".LBB0_15", 191, 205),
|
|
(".LBB0_16", 206, 208),
|
|
(".LBB0_17", 209, 222),
|
|
(".LBB0_18", 222, 228),
|
|
(".LBB0_19", 229, 261),
|
|
(".LBB0_20", 261, 269),
|
|
(".LBB0_22", 273, 280),
|
|
(".LBB0_24", 284, 286),
|
|
(".LBB0_26", 291, 293),
|
|
(".LBB0_28", 299, 307),
|
|
(".LBB0_29", 307, 444),
|
|
(".LBB0_31", 449, 459),
|
|
(".LBB0_32", 459, 480),
|
|
(".LBB0_33", 481, 484),
|
|
(".LBB0_34", 485, 494),
|
|
(".LBB0_35", 494, 504),
|
|
(".LBB0_36", 505, 508),
|
|
(".LBB0_37", 509, 518),
|
|
(".LBB0_38", 519, 568),
|
|
("main", 575, 590),
|
|
],
|
|
)
|
|
|
|
def test_find_basic_loop_body(self):
|
|
self.assertEqual(
|
|
[
|
|
(k, v[0].line_number, v[-1].line_number)
|
|
for k, v in find_basic_loop_bodies(self.parsed_x86_att).items()
|
|
],
|
|
[(".L4", 66, 74), (".L10", 146, 154), (".L28", 290, 300)],
|
|
)
|
|
|
|
self.assertEqual(
|
|
[
|
|
(k, v[0].line_number, v[-1].line_number)
|
|
for k, v in find_basic_loop_bodies(self.parsed_AArch).items()
|
|
],
|
|
[
|
|
(".LBB0_12", 134, 173),
|
|
(".LBB0_15", 191, 205),
|
|
(".LBB0_18", 222, 228),
|
|
(".LBB0_29", 307, 444),
|
|
(".LBB0_32", 459, 480),
|
|
(".LBB0_35", 494, 504),
|
|
],
|
|
)
|
|
|
|
##################
|
|
# Helper functions
|
|
##################
|
|
|
|
@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(TestMarkerUtils)
|
|
unittest.TextTestRunner(verbosity=2).run(suite)
|