363 lines
11 KiB
Python
363 lines
11 KiB
Python
import copy
|
|
|
|
from ScEpTIC.AST.elements.instruction import Instruction
|
|
from ScEpTIC.AST.elements.instructions.other_operations import CallOperation
|
|
from ScEpTIC.emulator.io.input import InputManager
|
|
from ScEpTIC.exceptions import RuntimeException
|
|
|
|
|
|
class RunInfo:
|
|
|
|
def __init__(self, io_type, pc, global_clock, run_id, function_name, io_name, io_value):
|
|
self.io_type = io_type
|
|
self.run_id = run_id
|
|
self.pc = copy.deepcopy(pc)
|
|
self.function_name = function_name
|
|
self.io_name = io_name
|
|
self.io_value = copy.deepcopy(io_value)
|
|
self.global_clock = global_clock
|
|
|
|
|
|
def __str__(self):
|
|
if self.io_type == 'VAR':
|
|
retval = '[{}]\nName: {}\nValue: {}\n'.format(self.io_type, self.io_name, self.io_value)
|
|
|
|
elif self.io_type == 'LOG':
|
|
retval = '[{}]\nName: {}\n'.format(self.io_type, self.io_name)
|
|
|
|
else:
|
|
retval = '[{}]\nName: {}\nFunction name: {}\nValue: {}\n'.format(self.io_type, self.io_name, self.function_name, self.io_value)
|
|
|
|
retval += 'Execution clock: {}\nRun id: {}\nInstruction program counter:\n{}'.format(self.global_clock, self.run_id, self.pc.resolve())
|
|
|
|
return retval
|
|
|
|
|
|
class ProfilingReset(Instruction):
|
|
"""
|
|
Reset operation for profiling purposes. Used as marker by the corresponding interruption manager.
|
|
"""
|
|
|
|
name = 'sceptic_reset'
|
|
|
|
def __init__(self, mode, value):
|
|
super().__init__()
|
|
|
|
self._omit_target = True
|
|
self.target = None
|
|
self.mode = mode
|
|
self.value = value
|
|
self.done = False
|
|
|
|
|
|
def _get_mode(self):
|
|
# get address of name var
|
|
address = self.mode.get_val()
|
|
gst = self._vmstate.memory.gst._get_gst_from_address(address)
|
|
return gst.read_string_from_address(address)
|
|
|
|
|
|
def __str__(self):
|
|
retstr = super().__str__()
|
|
retstr += 'ProfilingReset({})'.format(self.mode)
|
|
return retstr
|
|
|
|
|
|
def get_val(self):
|
|
return None
|
|
|
|
|
|
def get_uses(self):
|
|
first_reg = self.mode.get_uses()
|
|
if self.value is None or 'get_uses' not in dir(self.value):
|
|
return first_reg
|
|
|
|
second_reg = self.value.get_uses()
|
|
return first_reg + second_reg
|
|
|
|
|
|
def replace_reg_name(self, old_reg_name, new_reg_name):
|
|
self.name.replace_reg_name(old_reg_name, new_reg_name)
|
|
|
|
if self.value is None or 'replace_reg_name' not in dir(self.value):
|
|
return
|
|
|
|
self.value.replace_reg_name(old_reg_name, new_reg_name)
|
|
|
|
|
|
def evaluate_reset_condition(self, power_failures):
|
|
mode = self._get_mode()
|
|
|
|
if mode == 'once':
|
|
if not self.done:
|
|
self.done = True
|
|
return True
|
|
|
|
return False
|
|
|
|
elif mode == 'clock':
|
|
target_clock = self.value.get_val()
|
|
return power_failures == target_clock
|
|
|
|
elif mode == 'conditional':
|
|
if not self.done:
|
|
condition = self.value.get_val()
|
|
|
|
if condition == 1:
|
|
self.done = True
|
|
|
|
return condition == 1
|
|
|
|
return False
|
|
|
|
if self.tick_count == 0:
|
|
self.tick_count += 1
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
class ProfilingLog(Instruction):
|
|
"""
|
|
Profiling log operation for profiling purposes. Used as marker by the corresponding interruption manager.
|
|
"""
|
|
|
|
tick_count = 0
|
|
name = 'sceptic_log'
|
|
|
|
tracking = None
|
|
power_failures = None
|
|
|
|
def __init__(self, name, variable):
|
|
super().__init__()
|
|
|
|
self._omit_target = True
|
|
self.target = None
|
|
self.name = name
|
|
self.variable = variable
|
|
|
|
|
|
def __str__(self):
|
|
retstr = super().__str__()
|
|
retstr += 'ProfilingLog({}, {})'.format(self.name, self.variable)
|
|
return retstr
|
|
|
|
|
|
def get_val(self):
|
|
run_info = self.get_run_info(self.power_failures)
|
|
self.tracking[self.power_failures].append(run_info)
|
|
|
|
return None
|
|
|
|
|
|
def get_run_info(self, run_id):
|
|
# get address of name var
|
|
address = self.name.get_val()
|
|
gst = self._vmstate.memory.gst._get_gst_from_address(address)
|
|
name = gst.read_string_from_address(address)
|
|
|
|
pc = copy.deepcopy(self._vmstate.register_file.pc)
|
|
global_clock = self._vmstate.global_clock
|
|
|
|
if self.variable is None:
|
|
return RunInfo('LOG', pc, global_clock, run_id, '', name, '')
|
|
|
|
else:
|
|
value = self.variable.get_val()
|
|
return RunInfo('VAR', pc, global_clock, run_id, '', name, value)
|
|
|
|
|
|
def get_uses(self):
|
|
first_reg = self.name.get_uses()
|
|
if self.variable is None or 'get_uses' not in dir(self.variable):
|
|
return first_reg
|
|
|
|
second_reg = self.variable.get_uses()
|
|
return first_reg + second_reg
|
|
|
|
|
|
def replace_reg_name(self, old_reg_name, new_reg_name):
|
|
self.name.replace_reg_name(old_reg_name, new_reg_name)
|
|
|
|
if self.variable is None or 'get_uses' not in dir(self.variable):
|
|
return
|
|
|
|
self.variable.replace_reg_name(old_reg_name, new_reg_name)
|
|
|
|
|
|
class ProfilingChangeInputVal(Instruction):
|
|
"""
|
|
Changes input value at runtime. Used as marker by the corresponding interruption manager.
|
|
"""
|
|
|
|
tick_count = 0
|
|
name = 'sceptic_change_input'
|
|
|
|
def __init__(self, name, value):
|
|
super().__init__()
|
|
|
|
self._omit_target = True
|
|
self.target = None
|
|
self.name = name
|
|
self.value = value
|
|
|
|
|
|
def __str__(self):
|
|
retstr = super().__str__()
|
|
retstr += 'ProfilingChangeInputVal({}, {})'.format(self.name, self.value)
|
|
return retstr
|
|
|
|
|
|
def get_val(self):
|
|
# get address of name var
|
|
address = self.name.get_val()
|
|
gst = self._vmstate.memory.gst._get_gst_from_address(address)
|
|
name = gst.read_string_from_address(address)
|
|
|
|
value = self.value.get_val()
|
|
|
|
InputManager.set_input_value(name, value)
|
|
|
|
return None
|
|
|
|
|
|
def get_uses(self):
|
|
first_reg = self.name.get_uses()
|
|
if self.value is None or 'get_uses' not in dir(self.value):
|
|
return first_reg
|
|
|
|
second_reg = self.value.get_uses()
|
|
return first_reg + second_reg
|
|
|
|
|
|
def replace_reg_name(self, old_reg_name, new_reg_name):
|
|
self.name.replace_reg_name(old_reg_name, new_reg_name)
|
|
|
|
if self.value is None or 'get_uses' not in dir(self.value):
|
|
return
|
|
|
|
self.value.replace_reg_name(old_reg_name, new_reg_name)
|
|
|
|
|
|
|
|
def pre_parse_profiling_log(text, ir_function_prefix):
|
|
"""
|
|
Pre parse profiling log, so to avoid errors due to the lack of profiling_log definition in the C source code.
|
|
"""
|
|
|
|
if ProfilingLog.name in text:
|
|
f_name = ProfilingLog.name
|
|
|
|
elif ProfilingReset.name in text:
|
|
f_name = ProfilingReset.name
|
|
|
|
elif ProfilingChangeInputVal.name in text:
|
|
f_name = ProfilingChangeInputVal.name
|
|
|
|
else:
|
|
return text
|
|
|
|
# if ProfilingLog or ProfilingReset
|
|
index = text.index(f_name)
|
|
|
|
if text[index-1] == ir_function_prefix:
|
|
name_index = text.index('@', index) + 1
|
|
name = text[name_index]
|
|
|
|
if '%' in text[name_index:]:
|
|
var_index = text.index('%', name_index) + 1
|
|
var = text[var_index]
|
|
|
|
return ['call', 'void', ir_function_prefix, f_name, '(', 'i8', '*', ir_function_prefix, name, ',', 'i32', '%', var, ')']
|
|
|
|
# search for an immediate as second parameter -> ), i32 N)
|
|
next_arg_search = text[name_index:]
|
|
if ')' in next_arg_search:
|
|
first_par = text[name_index:].index(')')
|
|
|
|
# -> , i32 N
|
|
if next_arg_search[first_par+1] == ',' and next_arg_search[first_par+2] == 'i32':
|
|
var = next_arg_search[first_par+3]
|
|
return ['call', 'void', ir_function_prefix, f_name, '(', 'i8', '*', ir_function_prefix, name, ',', 'i32', var, ')']
|
|
|
|
return ['call', 'void', ir_function_prefix, f_name, '(', 'i8', '*', ir_function_prefix, name, ')']
|
|
|
|
|
|
def process_profiling_calls(ir_function_prefix, functions, declarations):
|
|
"""
|
|
Processes the calls to profiling operations in each function.
|
|
"""
|
|
|
|
# adjust function names with prefix
|
|
ProfilingReset.name = '{}{}'.format(ir_function_prefix, ProfilingReset.name)
|
|
ProfilingLog.name = '{}{}'.format(ir_function_prefix, ProfilingLog.name)
|
|
ProfilingChangeInputVal.name = '{}{}'.format(ir_function_prefix, ProfilingChangeInputVal.name)
|
|
|
|
profiling_f_names = [ProfilingReset.name, ProfilingLog.name, ProfilingChangeInputVal.name]
|
|
|
|
for f_name in profiling_f_names:
|
|
if f_name in declarations:
|
|
del declarations[f_name]
|
|
|
|
# for each function scan the body and
|
|
for function_name in functions:
|
|
function = functions[function_name]
|
|
|
|
i = 0
|
|
|
|
# scan and alter the code in function body.
|
|
while i < len(function.body):
|
|
line = function.body[i]
|
|
|
|
# replace in function body the calls to profiling function with the corresponding operation.
|
|
if isinstance(line, CallOperation) and line.name in profiling_f_names:
|
|
function.body[i] = _create_profiling_call(line)
|
|
|
|
i += 1
|
|
|
|
# update function labels
|
|
function.update_labels()
|
|
|
|
# remove from internal calls the profiling functions, so to not consider them during registers allocation
|
|
for f_name in profiling_f_names:
|
|
function._remove_from_calls(f_name)
|
|
|
|
|
|
def _create_profiling_call(line):
|
|
"""
|
|
Given a function call to a Profiling function, returns the corresponding operation.
|
|
"""
|
|
|
|
# check the length of the arguments passed to the function call.
|
|
|
|
args_number = len(line.function_args)
|
|
|
|
exception_message = 'Wrong parameter count for {} function. They must be a string and a variable pointer!'.format(line.name)
|
|
|
|
if line.name == ProfilingLog.name:
|
|
if args_number == 0 or args_number > 2:
|
|
raise RuntimeException(exception_message)
|
|
|
|
profiling_function = ProfilingLog(line.function_args[0], line.function_args[1] if args_number == 2 else None)
|
|
|
|
elif line.name == ProfilingReset.name:
|
|
if args_number == 0 or args_number > 2:
|
|
raise RuntimeException(exception_message)
|
|
|
|
profiling_function = ProfilingReset(line.function_args[0], line.function_args[1] if args_number == 2 else None)
|
|
|
|
elif line.name == ProfilingChangeInputVal.name:
|
|
if args_number != 2:
|
|
raise RuntimeException(exception_message)
|
|
|
|
profiling_function = ProfilingChangeInputVal(line.function_args[0], line.function_args[1])
|
|
|
|
# copy class parameters
|
|
profiling_function.basic_block_id = line.basic_block_id
|
|
profiling_function.label = line.label
|
|
profiling_function.preds = line.preds
|
|
profiling_function.metadata = line.metadata
|
|
|
|
# return the created object
|
|
return profiling_function
|