blob: f88ad21db1b61c8b82bea4aa46f897f7ce795376 [file] [log] [blame]
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
import py
import os
from sim.standalonesim import StandaloneSim
from sim.stats import ExecutionStats
import testutil
def _run_sim_for_stats(sim: StandaloneSim) -> ExecutionStats:
sim.run(verbose=False, dump_file=None)
# Ensure that the execution was successful.
assert sim.state.ext_regs.read('ERR_BITS', False) == 0
assert sim.stats
return sim.stats
def _simulate_asm_file(asm_file: str, tmpdir: py.path.local) -> ExecutionStats:
'''Run the OTBN simulator, collect statistics, and return them.'''
sim = testutil.prepare_sim_for_asm_file(asm_file, tmpdir, True)
return _run_sim_for_stats(sim)
def _simulate_asm_str(assembly: str, tmpdir: py.path.local) -> ExecutionStats:
sim = testutil.prepare_sim_for_asm_str(assembly, tmpdir, True)
return _run_sim_for_stats(sim)
def test_basic_block_stats(tmpdir: py.path.local) -> None:
'''Check if statistics for basic blocks are calculated correctly.'''
asm = """
jump:
/* A basic block of 4 instructions, ending with a jump. */
addi x7, x7, 1
addi x7, x7, 2
addi x7, x7, 3
jal x0, branch1
nop /* Should never be executed. */
branch1:
/* A basic block of 3 instructions, ending with a branch. */
addi x7, x7, 4
addi x7, x7, -10
beq x7, x0, branch2
branch2:
/* A basic block of 3 instructions, ending with a branch. */
addi x7, x7, 4
addi x7, x7, -4
beq x7, x0, exit
nop /* Should never be executed. */
exit:
ecall
"""
stats = _simulate_asm_str(asm, tmpdir)
assert stats.get_insn_count() == 11
assert sorted(stats.basic_block_histo) == sorted({
4: 1, # 1 basic block with 4 instructions (jump)
3: 2, # 2 basic blocks with 3 instructions (branch1 and branch2)
1: 1 # 1 basic block with 1 instruction (exit)
})
assert sorted(stats.ext_basic_block_histo) == sorted({
7: 1, # 1 ext. basic block with 7 instructions (jump + branch1)
3: 2, # 1 ext. basic block with 3 instructions (branch2)
1: 1 # 1 ext. basic block with 1 instruction (exit)
})
def test_basic_block_stats_loop(tmpdir: py.path.local) -> None:
'''Check if statistics for basic blocks LOOPs are calculated properly.'''
asm = """
/* Loop x7 == 3 times over a body of 2 instructions. */
addi x7, x0, 3
loop x7, 2
nop
nop
ecall
"""
stats = _simulate_asm_str(asm, tmpdir)
assert stats.get_insn_count() == 9
assert sorted(stats.basic_block_histo) == sorted({
4: 1, # 1 basic block with 4 instructions (addi + loop + 2x nop)
2: 1, # 1 basic block with 2 instructions (loop body on second iter.)
1: 1 # 1 basic block with 1 instruction (ecall)
})
assert (sorted(stats.ext_basic_block_histo) ==
sorted(stats.basic_block_histo))
def test_basic_block_stats_loopi(tmpdir: py.path.local) -> None:
'''Check statistics for basic blocks in LOOPIs are calculated properly'''
asm = """
/* Loop 3 times over a body of 2 instructions. */
loopi 3, 2
nop
nop
ecall
"""
stats = _simulate_asm_str(asm, tmpdir)
assert stats.get_insn_count() == 8
assert sorted(stats.basic_block_histo) == sorted({
3: 1, # 1 basic block with 4 instructions (addi + loop + 2x nop)
2: 1, # 1 basic block with 2 instructions (loop body on second iter.)
1: 1 # 1 basic block with 1 instruction (ecall)
})
assert sorted(stats.ext_basic_block_histo) == sorted({
8: 1 # All instructions are statically determined.
})
def test_general_and_loop(tmpdir: py.path.local) -> None:
'''Test the collection of general statistics as well as loop stats.'''
asm_file = os.path.join(os.path.dirname(__file__),
'simple', 'loops', 'loops.s')
stats = _simulate_asm_file(asm_file, tmpdir)
# General statistics
assert stats.stall_count == 3
assert stats.get_insn_count() == 28
assert stats.insn_histo == {'addi': 22, 'loop': 4, 'loopi': 1, 'ecall': 1}
assert stats.func_calls == []
# Loop statistics.
exp = [
# Outer LOOPI
{'iterations': 4, 'loop_addr': 8, 'loop_len': 4},
# Inner LOOP
{'iterations': 3, 'loop_addr': 16, 'loop_len': 1},
{'iterations': 3, 'loop_addr': 16, 'loop_len': 1},
{'iterations': 3, 'loop_addr': 16, 'loop_len': 1},
{'iterations': 3, 'loop_addr': 16, 'loop_len': 1}
]
assert stats.loops == exp
def test_func_call_direct(tmpdir: py.path.local) -> None:
'''Test the collection of statistics related to loops.'''
asm_file = os.path.join(os.path.dirname(__file__),
'simple', 'subroutines', 'direct-call.s')
stats = _simulate_asm_file(asm_file, tmpdir)
exp = [{'call_site': 4, 'callee_func': 12, 'caller_func': 0}]
assert stats.func_calls == exp
def test_func_call_indirect(tmpdir: py.path.local) -> None:
'''Test the collection of statistics related to loops.'''
asm_file = os.path.join(os.path.dirname(__file__),
'simple', 'subroutines', 'indirect-call.s')
stats = _simulate_asm_file(asm_file, tmpdir)
exp = [{'call_site': 8, 'callee_func': 16, 'caller_func': 0}]
assert stats.func_calls == exp