Merge branch 'master' into A64FX

This commit is contained in:
JanLJL
2020-10-15 22:44:12 +02:00
22 changed files with 33430 additions and 53011 deletions

View File

@@ -57,8 +57,12 @@ Additional requirements are:
- `Python3 <https://www.python.org/>`__ - `Python3 <https://www.python.org/>`__
- `Graphviz <https://www.graphviz.org/>`__ for dependency graph creation (minimal dependency is `libgraphviz-dev` on Ubuntu) - `Graphviz <https://www.graphviz.org/>`__ for dependency graph creation (minimal dependency is `libgraphviz-dev` on Ubuntu)
Optional requirements are:
- `Kerncraft <https://github.com/RRZE-HPC/kerncraft>`__ >=v0.8.4 for marker insertion - `Kerncraft <https://github.com/RRZE-HPC/kerncraft>`__ >=v0.8.4 for marker insertion
- `ibench <https://github.com/RRZE-HPC/ibench>`__ or `asmbench <https://github.com/RRZE-HPC/asmbench/>`__ for throughput/latency measurements - `ibench <https://github.com/RRZE-HPC/ibench>`__ or `asmbench <https://github.com/RRZE-HPC/asmbench/>`__ for throughput/latency measurements
- `BeautifulSoup4 <https://www.crummy.com/software/BeautifulSoup/bs4/doc/>`__ for scraping instruction form information for the x86 ISA (experimental)
Design Design
====== ======

View File

@@ -1,6 +1,6 @@
"""Open Source Architecture Code Analyzer""" """Open Source Architecture Code Analyzer"""
name = 'osaca' name = 'osaca'
__version__ = '0.3.3.dev0' __version__ = '0.3.7'
# To trigger travis deployment to pypi, do the following: # To trigger travis deployment to pypi, do the following:
# 1. Increment __version___ # 1. Increment __version___

View File

@@ -1,4 +1,4 @@
osaca_version: 0.3.2 osaca_version: 0.3.4
micro_architecture: Intel Broadwell micro_architecture: Intel Broadwell
arch_code: BDW arch_code: BDW
isa: x86 isa: x86

View File

@@ -1,4 +1,4 @@
osaca_version: 0.3.2 osaca_version: 0.3.4
micro_architecture: Cascade Lake SP micro_architecture: Cascade Lake SP
arch_code: CSX arch_code: CSX
isa: x86 isa: x86

View File

@@ -1,4 +1,4 @@
osaca_version: 0.3.2 osaca_version: 0.3.4
micro_architecture: Intel Haswell micro_architecture: Intel Haswell
arch_code: HSW arch_code: HSW
isa: x86 isa: x86

View File

@@ -1,4 +1,4 @@
osaca_version: 0.3.0 osaca_version: 0.3.4
isa: "AArch64" isa: "AArch64"
# Contains all operand-irregular instruction forms OSACA supports for AArch64. # Contains all operand-irregular instruction forms OSACA supports for AArch64.
# Operand-regular for a AArch64 instruction form with N operands in the shape of # Operand-regular for a AArch64 instruction form with N operands in the shape of
@@ -191,3 +191,73 @@ instruction_forms:
post-indexed: "*" post-indexed: "*"
source: false source: false
destination: true destination: true
- name: cmp
operands:
- class: "register"
prefix: "*"
source: true
destination: false
- class: "register"
prefix: "*"
source: true
destination: false
- name: cmp
operands:
- class: "register"
prefix: "*"
source: true
destination: false
- class: "immediate"
imd: "int"
source: true
destination: false
- name: cmn
operands:
- class: "register"
prefix: "*"
source: true
destination: false
- class: "register"
prefix: "*"
source: true
destination: false
- name: cmn
operands:
- class: "register"
prefix: "*"
source: true
destination: false
- class: "immediate"
imd: "int"
source: true
destination: false
- name: fcmp
operands:
- class: "register"
prefix: "*"
source: true
destination: false
- class: "register"
prefix: "*"
source: true
destination: false
- name: fcmp
operands:
- class: "register"
prefix: "*"
source: true
destination: false
- class: "immediate"
imd: "double"
source: true
destination: false
- name: fcmp
operands:
- class: "register"
prefix: "*"
source: true
destination: false
- class: "immediate"
imd: "float"
source: true
destination: false

View File

@@ -1,4 +1,4 @@
osaca_version: 0.3.0 osaca_version: 0.3.4
isa: "x86" isa: "x86"
# Contains all operand-irregular instruction forms OSACA supports for x86. # Contains all operand-irregular instruction forms OSACA supports for x86.
# Operand-regular for a x86 AT&T instruction form with N operands in the shape of # Operand-regular for a x86 AT&T instruction form with N operands in the shape of
@@ -3167,7 +3167,7 @@ instruction_forms:
destination: false destination: false
hidden_operands: hidden_operands:
- class: "memory" - class: "memory"
base: "gpr" base: {name: 'rsp'}
offset: ~ offset: ~
index: ~ index: ~
scale: 1 scale: 1
@@ -3177,11 +3177,29 @@ instruction_forms:
name: "rsp" name: "rsp"
source: true source: true
destination: true destination: true
- name: pop
operands:
- class: "register"
name: "gpr"
source: false
destination: true
hidden_operands:
- class: "memory"
base: {name: 'rsp'}
offset: ~
index: ~
scale: 1
source: true
destination: false
- class: "register"
name: "rsp"
source: true
destination: true
- name: pushfq - name: pushfq
operands: [] operands: []
hidden_operands: hidden_operands:
- class: "memory" - class: "memory"
base: "gpr" base: {name: 'rsp'}
offset: ~ offset: ~
index: ~ index: ~
scale: 1 scale: 1
@@ -3971,4 +3989,3 @@ instruction_forms:
name: "gpr" name: "gpr"
source: true source: true
destination: true destination: true

File diff suppressed because it is too large Load Diff

View File

@@ -219,9 +219,15 @@ def extract_model(tree, arch, skip_mem=True):
port_23 = True port_23 = True
if '4' in pp[1]: if '4' in pp[1]:
port_4 = True port_4 = True
# Add (1, ['2D', '3D']) if load ports (2 & 3) are used, but not the store port (4) # Add (X, ['2D', '3D']) if load ports (2 & 3) are used, but not the store port (4)
# X = 2 on SNB and IVB IFF used in combination with ymm register, otherwise X = 1
if arch.upper() in ['SNB', 'IVB'] and \
any([p['class'] == 'register' and p['name'] == 'ymm' for p in parameters]):
data_port_throughput = 2
else:
data_port_throughput = 1
if port_23 and not port_4: if port_23 and not port_4:
port_pressure.append((1, ['2D', '3D'])) port_pressure.append((data_port_throughput, ['2D', '3D']))
# Add missing ports: # Add missing ports:
for ports in [pp[1] for pp in port_pressure]: for ports in [pp[1] for pp in port_pressure]:
@@ -275,7 +281,7 @@ def main():
if model is not None: if model is not None:
print( print(
rhs_comment( rhs_comment(
model.dump(), basename + " " + args.xml.split('/')[-1] + " " + args.arch model.dump(), "uops.info import"
) )
) )
else: else:

View File

@@ -1,4 +1,4 @@
osaca_version: 0.3.3 osaca_version: 0.3.4
micro_architecture: Arm Neoverse N1 micro_architecture: Arm Neoverse N1
arch_code: n1 arch_code: n1
isa: AArch64 isa: AArch64

View File

@@ -1,4 +1,4 @@
osaca_version: 0.3.2 osaca_version: 0.3.4
micro_architecture: Intel Skylake SP micro_architecture: Intel Skylake SP
arch_code: SKX arch_code: SKX
isa: x86 isa: x86

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
osaca_version: 0.3.2 osaca_version: 0.3.4
micro_architecture: Thunder X2 micro_architecture: Thunder X2
arch_code: tx2 arch_code: tx2
isa: AArch64 isa: AArch64
@@ -403,6 +403,22 @@ instruction_forms:
throughput: 1.0 throughput: 1.0
latency: 4.0 # 2*p34 latency: 4.0 # 2*p34
port_pressure: [[2.0, '34']] port_pressure: [[2.0, '34']]
- name: ldp
operands:
- class: register
prefix: d
- class: register
prefix: d
- class: memory
base: x
offset: '*'
index: '*'
scale: '*'
pre-indexed: false
post-indexed: false
throughput: 1.0
latency: 4.0 # 2*p34
port_pressure: [[2.0, '34']]
- name: ldp - name: ldp
operands: operands:
- class: register - class: register

View File

@@ -1,4 +1,4 @@
osaca_version: 0.3.2 osaca_version: 0.3.4
micro_architecture: AMD Zen (family 17h) micro_architecture: AMD Zen (family 17h)
arch_code: ZEN1 arch_code: ZEN1
isa: x86 isa: x86

View File

@@ -1,4 +1,4 @@
osaca_version: 0.3.2 osaca_version: 0.3.4
micro_architecture: AMD Zen2 micro_architecture: AMD Zen2
arch_code: ZEN2 arch_code: ZEN2
isa: x86 isa: x86

View File

@@ -142,6 +142,12 @@ def create_parser(parser=None):
parser.add_argument( parser.add_argument(
'--verbose', '-v', action='count', default=0, help='Increases verbosity level.' '--verbose', '-v', action='count', default=0, help='Increases verbosity level.'
) )
parser.add_argument(
'--out', '-o',
default=sys.stdout,
type=argparse.FileType('w'),
help='Write analysis to this file (default to stdout).'
)
parser.add_argument( parser.add_argument(
'file', type=argparse.FileType('r'), help='Path to object (ASM or instruction file).' 'file', type=argparse.FileType('r'), help='Path to object (ASM or instruction file).'
) )
@@ -326,7 +332,7 @@ def main():
parser = create_parser() parser = create_parser()
args = parser.parse_args() args = parser.parse_args()
check_arguments(args, parser) check_arguments(args, parser)
run(args) run(args, output_file=args.out)
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -8,6 +8,7 @@ class BaseParser(object):
DIRECTIVE_ID = 'directive' DIRECTIVE_ID = 'directive'
IMMEDIATE_ID = 'immediate' IMMEDIATE_ID = 'immediate'
LABEL_ID = 'label' LABEL_ID = 'label'
IDENTIFIER_ID = 'identifier'
MEMORY_ID = 'memory' MEMORY_ID = 'memory'
REGISTER_ID = 'register' REGISTER_ID = 'register'
SEGMENT_EXT_ID = 'segment_extension' SEGMENT_EXT_ID = 'segment_extension'

View File

@@ -19,22 +19,23 @@ class ParserAArch64(BaseParser):
pp.ZeroOrMore(pp.Word(pp.printables)) pp.ZeroOrMore(pp.Word(pp.printables))
).setResultsName(self.COMMENT_ID) ).setResultsName(self.COMMENT_ID)
# Define ARM assembly identifier # Define ARM assembly identifier
decimal_number = pp.Combine(
pp.Optional(pp.Literal('-')) + pp.Word(pp.nums)
).setResultsName('value')
hex_number = pp.Combine(pp.Literal('0x') + pp.Word(pp.hexnums)).setResultsName('value')
relocation = pp.Combine(pp.Literal(':') + pp.Word(pp.alphanums + '_') + pp.Literal(':')) relocation = pp.Combine(pp.Literal(':') + pp.Word(pp.alphanums + '_') + pp.Literal(':'))
first = pp.Word(pp.alphas + '_.', exact=1) first = pp.Word(pp.alphas + '_.', exact=1)
rest = pp.Word(pp.alphanums + '_.') rest = pp.Word(pp.alphanums + '_.')
identifier = pp.Group( identifier = pp.Group(
pp.Optional(relocation).setResultsName('relocation') pp.Optional(relocation).setResultsName('relocation')
+ pp.Combine(first + pp.Optional(rest)).setResultsName('name') + pp.Combine(first + pp.Optional(rest)).setResultsName('name')
).setResultsName('identifier') + pp.Optional(pp.Suppress(pp.Literal('+')) + (hex_number | decimal_number).setResultsName('offset'))
).setResultsName(self.IDENTIFIER_ID)
# Label # Label
self.label = pp.Group( self.label = pp.Group(
identifier.setResultsName('name') + pp.Literal(':') + pp.Optional(self.comment) identifier.setResultsName('name') + pp.Literal(':') + pp.Optional(self.comment)
).setResultsName(self.LABEL_ID) ).setResultsName(self.LABEL_ID)
# Directive # Directive
decimal_number = pp.Combine(
pp.Optional(pp.Literal('-')) + pp.Word(pp.nums)
).setResultsName('value')
hex_number = pp.Combine(pp.Literal('0x') + pp.Word(pp.hexnums)).setResultsName('value')
directive_option = pp.Combine( directive_option = pp.Combine(
pp.Word(pp.alphas + '#@.%', exact=1) pp.Word(pp.alphas + '#@.%', exact=1)
+ pp.Optional(pp.Word(pp.printables + ' ', excludeChars=',')) + pp.Optional(pp.Word(pp.printables + ' ', excludeChars=','))
@@ -341,6 +342,8 @@ class ParserAArch64(BaseParser):
return self.process_immediate(operand[self.IMMEDIATE_ID]) return self.process_immediate(operand[self.IMMEDIATE_ID])
if self.LABEL_ID in operand: if self.LABEL_ID in operand:
return self.process_label(operand[self.LABEL_ID]) return self.process_label(operand[self.LABEL_ID])
if self.IDENTIFIER_ID in operand:
return self.process_identifier(operand[self.IDENTIFIER_ID])
return operand return operand
def process_memory_address(self, memory_address): def process_memory_address(self, memory_address):
@@ -424,6 +427,13 @@ class ParserAArch64(BaseParser):
label['name'] = label['name']['name'] label['name'] = label['name']['name']
return AttrDict({self.LABEL_ID: label}) return AttrDict({self.LABEL_ID: label})
def process_identifier(self, identifier):
"""Post-process identifier operand"""
# remove value if it consists of symbol+offset
if 'value' in identifier:
del identifier['value']
return AttrDict({self.IDENTIFIER_ID: identifier})
def get_full_reg_name(self, register): def get_full_reg_name(self, register):
"""Return one register name string including all attributes""" """Return one register name string including all attributes"""
if 'lanes' in register: if 'lanes' in register:

View File

@@ -1,6 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import re
import string import string
import pyparsing as pp import pyparsing as pp
@@ -34,8 +33,20 @@ class ParserX86ATT(BaseParser):
+ pp.Optional(relocation).setResultsName('relocation') + pp.Optional(relocation).setResultsName('relocation')
).setResultsName('identifier') ).setResultsName('identifier')
# Label # Label
rest = pp.Word(pp.alphanums + '$_.+-()')
label_identifier = pp.Group(
pp.Optional(id_offset).setResultsName('offset')
+ pp.Combine(first + pp.Optional(rest)).setResultsName('name')
+ pp.Optional(relocation).setResultsName('relocation')
).setResultsName('identifier')
numeric_identifier = pp.Group(
pp.Word(pp.nums).setResultsName('name')
+ pp.Optional(pp.oneOf('b f', caseless=True).setResultsName('suffix'))
).setResultsName('identifier')
self.label = pp.Group( self.label = pp.Group(
identifier.setResultsName('name') + pp.Literal(':') + pp.Optional(self.comment) (label_identifier | numeric_identifier).setResultsName('name')
+ pp.Literal(':')
+ pp.Optional(self.comment)
).setResultsName(self.LABEL_ID) ).setResultsName(self.LABEL_ID)
# Register: pp.Regex('^%[0-9a-zA-Z]+{}{z},?') # Register: pp.Regex('^%[0-9a-zA-Z]+{}{z},?')
self.register = pp.Group( self.register = pp.Group(
@@ -44,7 +55,7 @@ class ParserX86ATT(BaseParser):
+ pp.Optional(pp.Literal('(') + pp.Word(pp.nums) + pp.Literal(')')) + pp.Optional(pp.Literal('(') + pp.Word(pp.nums) + pp.Literal(')'))
+ pp.Optional( + pp.Optional(
pp.Literal('{') pp.Literal('{')
+ pp.Literal('%') + pp.Optional(pp.Suppress(pp.Literal('%')))
+ pp.Word(pp.alphanums).setResultsName('mask') + pp.Word(pp.alphanums).setResultsName('mask')
+ pp.Literal('}') + pp.Literal('}')
+ pp.Optional( + pp.Optional(
@@ -99,7 +110,7 @@ class ParserX86ATT(BaseParser):
+ pp.Literal(')') + pp.Literal(')')
+ pp.Optional( + pp.Optional(
pp.Literal('{') pp.Literal('{')
+ pp.Literal('%') + pp.Optional(pp.Suppress(pp.Literal('%')))
+ pp.Word(pp.alphanums).setResultsName('mask') + pp.Word(pp.alphanums).setResultsName('mask')
+ pp.Literal('}') + pp.Literal('}')
) )
@@ -132,7 +143,9 @@ class ParserX86ATT(BaseParser):
pp.alphanums pp.alphanums
).setResultsName('mnemonic') ).setResultsName('mnemonic')
# Combine to instruction form # Combine to instruction form
operand_first = pp.Group(self.register ^ immediate ^ memory ^ identifier) operand_first = pp.Group(
self.register ^ immediate ^ memory ^ identifier ^ numeric_identifier
)
operand_rest = pp.Group(self.register ^ immediate ^ memory) operand_rest = pp.Group(self.register ^ immediate ^ memory)
self.instruction_parser = ( self.instruction_parser = (
mnemonic mnemonic
@@ -305,7 +318,7 @@ class ParserX86ATT(BaseParser):
def process_label(self, label): def process_label(self, label):
"""Post-process label asm line""" """Post-process label asm line"""
# remove duplicated 'name' level due to identifier # remove duplicated 'name' level due to identifier
label['name'] = label['name']['name'] label['name'] = label['name'][0]['name']
return AttrDict({self.LABEL_ID: label}) return AttrDict({self.LABEL_ID: label})
def process_immediate(self, immediate): def process_immediate(self, immediate):

View File

@@ -494,6 +494,7 @@ class MachineModel(object):
if 'class' in operand: if 'class' in operand:
# compare two DB entries # compare two DB entries
return self._compare_db_entries(i_operand, operand) return self._compare_db_entries(i_operand, operand)
# TODO support class wildcards
# register # register
if 'register' in operand: if 'register' in operand:
if i_operand['class'] != 'register': if i_operand['class'] != 'register':
@@ -505,12 +506,14 @@ class MachineModel(object):
return False return False
return self._is_AArch64_mem_type(i_operand, operand['memory']) return self._is_AArch64_mem_type(i_operand, operand['memory'])
# immediate # immediate
# TODO support wildcards
if 'value' in operand or ('immediate' in operand and 'value' in operand['immediate']): if 'value' in operand or ('immediate' in operand and 'value' in operand['immediate']):
return i_operand['class'] == 'immediate' and i_operand['imd'] == 'int' return i_operand['class'] == 'immediate' and i_operand['imd'] == 'int'
if 'float' in operand or ('immediate' in operand and 'float' in operand['immediate']): if 'float' in operand or ('immediate' in operand and 'float' in operand['immediate']):
return i_operand['class'] == 'immediate' and i_operand['imd'] == 'float' return i_operand['class'] == 'immediate' and i_operand['imd'] == 'float'
if 'double' in operand or ('immediate' in operand and 'double' in operand['immediate']): if 'double' in operand or ('immediate' in operand and 'double' in operand['immediate']):
return i_operand['class'] == 'immediate' and i_operand['imd'] == 'double' return i_operand['class'] == 'immediate' and i_operand['imd'] == 'double'
# identifier
if 'identifier' in operand or ( if 'identifier' in operand or (
'immediate' in operand and 'identifier' in operand['immediate'] 'immediate' in operand and 'identifier' in operand['immediate']
): ):

View File

@@ -22,9 +22,9 @@ def reduce_to_section(kernel, isa):
else: else:
raise ValueError('ISA not supported.') raise ValueError('ISA not supported.')
if start == -1: if start == -1:
raise LookupError('Could not find START MARKER. Make sure it is inserted!') start = 0
if end == -1: if end == -1:
raise LookupError('Could not find END MARKER. Make sure it is inserted!') end = len(kernel)
return kernel[start:end] return kernel[start:end]

View File

@@ -178,120 +178,115 @@ class TestMarkerUtils(unittest.TestCase):
def test_marker_special_cases_AArch(self): def test_marker_special_cases_AArch(self):
bytes_line = '.byte 213,3,32,31\n' bytes_line = '.byte 213,3,32,31\n'
mov_start = 'mov x1, #111\n' start_marker = 'mov x1, #111\n' + bytes_line
mov_end = 'mov x1, #222\n' end_marker = 'mov x1, #222\n' + bytes_line
prologue = 'dup v0.2d, x14\n' + ' neg x9, x9\n' + ' .p2align 6\n' prologue = (
'dup v0.2d, x14\n'
'neg x9, x9\n'
'.p2align 6\n')
kernel = ( kernel = (
'.LBB0_28:\n' '.LBB0_28:\n'
+ 'fmul v7.2d, v7.2d, v19.2d\n' + 'fmul v7.2d, v7.2d, v19.2d\n'
+ 'stp q0, q1, [x10, #-32]\n' + 'stp q0, q1, [x10, #-32]\n'
+ 'b.ne .LBB0_28\n' + 'b.ne .LBB0_28\n')
) epilogue = (
epilogue = '.LBB0_29: // Parent Loop BB0_20 Depth=1\n' + 'bl dummy\n' '.LBB0_29: // Parent Loop BB0_20 Depth=1\n'
kernel_length = len(list(filter(None, kernel.split('\n')))) 'bl dummy\n')
# marker directly at the beginning samples = [
code_beginning = mov_start + bytes_line + kernel + mov_end + bytes_line + epilogue # (test name,
beginning_parsed = self.parser_AArch.parse_file(code_beginning) # ignored prologue, section to be extraced, ignored epilogue)
test_kernel = reduce_to_section(beginning_parsed, 'AArch64') ("markers",
self.assertEqual(len(test_kernel), kernel_length) prologue + start_marker, kernel, end_marker + epilogue),
kernel_start = len(list(filter(None, (mov_start + bytes_line).split('\n')))) ("marker at file start",
parsed_kernel = self.parser_AArch.parse_file(kernel, start_line=kernel_start) start_marker, kernel, end_marker + epilogue),
self.assertEqual(test_kernel, parsed_kernel) ("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),
]
# marker at the end for test_name, pro, kernel, epi in samples:
code_end = prologue + mov_start + bytes_line + kernel + mov_end + bytes_line + epilogue code = pro + kernel + epi
end_parsed = self.parser_AArch.parse_file(code_end) parsed = self.parser_AArch.parse_file(code)
test_kernel = reduce_to_section(end_parsed, 'AArch64') test_kernel = reduce_to_section(parsed, 'AArch64')
self.assertEqual(len(test_kernel), kernel_length) if kernel:
kernel_start = len(list(filter(None, (prologue + mov_start + bytes_line).split('\n')))) kernel_length = len(kernel.strip().split('\n'))
parsed_kernel = self.parser_AArch.parse_file(kernel, start_line=kernel_start) else:
self.assertEqual(test_kernel, parsed_kernel) kernel_length = 0
self.assertEqual(
# no kernel len(test_kernel), kernel_length,
code_empty = prologue + mov_start + bytes_line + mov_end + bytes_line + epilogue msg="Invalid exctracted kernel length on {!r} sample".format(test_name))
empty_parsed = self.parser_AArch.parse_file(code_empty) if pro:
test_kernel = reduce_to_section(empty_parsed, 'AArch64') kernel_start = len((pro).strip().split('\n'))
self.assertEqual(len(test_kernel), 0) else:
kernel_start = len(list(filter(None, (prologue + mov_start + bytes_line).split('\n')))) kernel_start = 0
self.assertEqual(test_kernel, []) parsed_kernel = self.parser_AArch.parse_file(kernel, start_line=kernel_start)
self.assertEqual(
# no start marker test_kernel, parsed_kernel,
code_no_start = prologue + bytes_line + kernel + mov_end + bytes_line + epilogue msg="Invalid exctracted kernel on {!r}".format(test_name))
no_start_parsed = self.parser_AArch.parse_file(code_no_start)
with self.assertRaises(LookupError):
reduce_to_section(no_start_parsed, 'AArch64')
# no end marker
code_no_end = prologue + mov_start + bytes_line + kernel + mov_end + epilogue
no_end_parsed = self.parser_AArch.parse_file(code_no_end)
with self.assertRaises(LookupError):
reduce_to_section(no_end_parsed, 'AArch64')
# no marker at all
code_no_marker = prologue + kernel + epilogue
no_marker_parsed = self.parser_AArch.parse_file(code_no_marker)
with self.assertRaises(LookupError):
reduce_to_section(no_marker_parsed, 'AArch64')
def test_marker_special_cases_x86(self): def test_marker_special_cases_x86(self):
bytes_line = '.byte 100\n.byte 103\n.byte 144\n' bytes_line = (
mov_start = 'movl $111, %ebx\n' '.byte 100\n'
mov_end = 'movl $222, %ebx\n' '.byte 103\n'
prologue = 'movl -88(%rbp), %r10d\n' + 'xorl %r11d, %r11d\n' + '.p2align 4,,10\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 = ( kernel = (
'.L3: #L3\n' '.L3: #L3\n'
+ 'vmovsd .LC1(%rip), %xmm0\n' 'vmovsd .LC1(%rip), %xmm0\n'
+ 'vmovsd %xmm0, (%r15,%rcx,8)\n' 'vmovsd %xmm0, (%r15,%rcx,8)\n'
+ 'cmpl %ecx, %ebx\n' 'cmpl %ecx, %ebx\n'
+ 'jle .L3\n' 'jle .L3\n')
) epilogue = (
epilogue = 'leaq -56(%rbp), %rsi\n' + 'movl %r10d, -88(%rbp)\n' + 'call timing\n' 'leaq -56(%rbp), %rsi\n'
kernel_length = len(list(filter(None, kernel.split('\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),
]
# marker directly at the beginning for test_name, pro, kernel, epi in samples:
code_beginning = mov_start + bytes_line + kernel + mov_end + bytes_line + epilogue code = pro + kernel + epi
beginning_parsed = self.parser_x86.parse_file(code_beginning) parsed = self.parser_x86.parse_file(code)
test_kernel = reduce_to_section(beginning_parsed, 'x86') test_kernel = reduce_to_section(parsed, 'x86')
self.assertEqual(len(test_kernel), kernel_length) if kernel:
kernel_start = len(list(filter(None, (mov_start + bytes_line).split('\n')))) kernel_length = len(kernel.strip().split('\n'))
parsed_kernel = self.parser_x86.parse_file(kernel, start_line=kernel_start) else:
self.assertEqual(test_kernel, parsed_kernel) kernel_length = 0
self.assertEqual(
# marker at the end len(test_kernel), kernel_length,
code_end = prologue + mov_start + bytes_line + kernel + mov_end + bytes_line + epilogue msg="Invalid exctracted kernel length on {!r} sample".format(test_name))
end_parsed = self.parser_x86.parse_file(code_end) if pro:
test_kernel = reduce_to_section(end_parsed, 'x86') kernel_start = len((pro).strip().split('\n'))
self.assertEqual(len(test_kernel), kernel_length) else:
kernel_start = len(list(filter(None, (prologue + mov_start + bytes_line).split('\n')))) kernel_start = 0
parsed_kernel = self.parser_x86.parse_file(kernel, start_line=kernel_start) parsed_kernel = self.parser_x86.parse_file(kernel, start_line=kernel_start)
self.assertEqual(test_kernel, parsed_kernel) self.assertEqual(
test_kernel, parsed_kernel,
# no kernel msg="Invalid exctracted kernel on {!r}".format(test_name))
code_empty = prologue + mov_start + bytes_line + mov_end + bytes_line + epilogue
empty_parsed = self.parser_x86.parse_file(code_empty)
test_kernel = reduce_to_section(empty_parsed, 'x86')
self.assertEqual(len(test_kernel), 0)
kernel_start = len(list(filter(None, (prologue + mov_start + bytes_line).split('\n'))))
self.assertEqual(test_kernel, [])
# no start marker
code_no_start = prologue + bytes_line + kernel + mov_end + bytes_line + epilogue
no_start_parsed = self.parser_x86.parse_file(code_no_start)
with self.assertRaises(LookupError):
reduce_to_section(no_start_parsed, 'x86')
# no end marker
code_no_end = prologue + mov_start + bytes_line + kernel + mov_end + epilogue
no_end_parsed = self.parser_x86.parse_file(code_no_end)
with self.assertRaises(LookupError):
reduce_to_section(no_end_parsed, 'x86')
# no marker at all
code_no_marker = prologue + kernel + epilogue
no_marker_parsed = self.parser_x86.parse_file(code_no_marker)
with self.assertRaises(LookupError):
reduce_to_section(no_marker_parsed, 'x86')
def test_find_jump_labels(self): def test_find_jump_labels(self):
self.assertEqual(find_jump_labels(self.parsed_x86), self.assertEqual(find_jump_labels(self.parsed_x86),