129 lines
5.6 KiB
Python
129 lines
5.6 KiB
Python
import copy
|
|
|
|
from ScEpTIC.AST.elements.instruction import Instruction
|
|
from ScEpTIC.exceptions import MemoryException
|
|
|
|
|
|
class SaveRegistersOperation(Instruction):
|
|
"""
|
|
Operation that emulates the saving of registers before a function call.
|
|
All the register savings are done as caller-save, since saving register in the callee
|
|
function will lead to multiple SaveRegistersOperation (one before each return).
|
|
The instruction is set up with:
|
|
- tick_count: consists in the number of registers used by the called function.
|
|
In a real scenario, 1 store operation (Rx -> stack) will be inserted for each register
|
|
that needs to be saved. Once this instruction is executed, the global clock will be incremented
|
|
by this value ( = number of store operations that should be present)
|
|
- target_reg: is the name of the register that will contain the return value of the call. It won't be restored, nor saved.
|
|
|
|
NB: all registers are saved from R0 to R_n, so it is sufficient to provide the number of registers to be saved
|
|
to save the first n registers.
|
|
"""
|
|
|
|
def __init__(self, tick_count, target_reg, register_pool):
|
|
super().__init__()
|
|
|
|
target_reg_id = register_pool._get_id_from_reg_name(target_reg)
|
|
|
|
if target_reg_id is None:
|
|
target_reg_id = tick_count
|
|
|
|
"""
|
|
Tick count corresponds to the number of registers to be saved, which
|
|
can be converted as a R_i with i in [0, tick_count)
|
|
If the target register is inside such range, tick_count should be decreased by 1
|
|
since the target register will be replaced by the function call and won't be restored
|
|
(it will contain the call's return value)
|
|
"""
|
|
if target_reg_id < tick_count:
|
|
tick_count -= 1
|
|
|
|
self.tick_count = tick_count
|
|
self.target_reg = target_reg
|
|
self._omit_target = True
|
|
|
|
|
|
def get_val(self):
|
|
"""
|
|
Save all registers, except for the target one.
|
|
They will be restored by RestoreRegistersOperation.
|
|
Since saving all registers or a sub-group of them won't change anything,
|
|
this function saves all of them in order to remove a control of which register
|
|
needs to be saved. The overall result is the same as it only some registers are saved,
|
|
since registers computed inside a function won't have any value needed by the caller,
|
|
except from the return register.
|
|
"""
|
|
|
|
# variables used to store registers
|
|
registers = {}
|
|
registers_input_lookup = {}
|
|
|
|
for reg in self._vmstate.register_file._registers:
|
|
if reg != self.target_reg:
|
|
reg = self._vmstate.register_file._registers[reg]
|
|
|
|
# save register's value
|
|
registers[reg.name] = copy.deepcopy(reg.value)
|
|
|
|
# save register's input lookup information
|
|
if self._vmstate.input_lookup_enabled:
|
|
input_lookup = copy.deepcopy(self._vmstate.register_file.get_input_lookup(reg.name))
|
|
if input_lookup is not None:
|
|
registers_input_lookup[reg.name] = input_lookup
|
|
|
|
# simulate register saving.
|
|
for _ in range(0, self.tick_count):
|
|
|
|
if self._vmstate.do_data_anomaly_check:
|
|
self._vmstate.function_call_lookup['regs'].append(self._vmstate.memory.stack.top_address)
|
|
|
|
self._vmstate.memory.stack.push(self._vmstate.memory.address_dimension, copy.deepcopy(registers), 'register')
|
|
|
|
# save registers into saving_pool
|
|
to_save = {'registers': registers, 'registers_input_lookup': registers_input_lookup}
|
|
self._vmstate.reg_saving_pool.append(to_save)
|
|
|
|
|
|
def __str__(self):
|
|
return 'SaveRegistersOperation(~{}) [{} ops]'.format(self.target_reg, self.tick_count)
|
|
|
|
|
|
class RestoreRegistersOperation(Instruction):
|
|
"""
|
|
Represents an operation that emulates the restoring of registers after a called function returns.
|
|
It is setup by proiding the corresponding SaveRegistersOperation, from which takes the tick_count and the
|
|
registers values to be restored.
|
|
"""
|
|
|
|
def __init__(self, save_registers_operation):
|
|
super().__init__()
|
|
|
|
self.tick_count = save_registers_operation.tick_count
|
|
self._omit_target = True
|
|
|
|
|
|
def get_val(self):
|
|
to_restore = self._vmstate.reg_saving_pool.pop()
|
|
|
|
# restore each register saved by the SaveRegistersOperation
|
|
# NB: the target register is not included here by construction of the
|
|
# SaveRegistersOperation method.
|
|
for reg in to_restore['registers']:
|
|
self._vmstate.register_file.write(reg, copy.deepcopy(to_restore['registers'][reg]))
|
|
|
|
# restore input lookup information.
|
|
if self._vmstate.input_lookup_enabled:
|
|
for reg in to_restore['registers_input_lookup']:
|
|
self._vmstate.register_file._input_lookup[reg] = copy.deepcopy(to_restore['registers_input_lookup'][reg])
|
|
|
|
# simulate registers restoring.
|
|
for _ in range(0, self.tick_count):
|
|
stacked_reg_file = self._vmstate.memory.stack.pop(self._vmstate.memory.address_dimension)
|
|
|
|
if to_restore['registers'] != stacked_reg_file:
|
|
raise MemoryException('[RestoreRegistersOperation] Register file saved in stack is anomalous (wrong register values).')
|
|
|
|
|
|
def __str__(self):
|
|
return 'RestoreRegistersOperation() [{} ops]'.format(self.tick_count)
|