2026-07-10 10:38:57 +02:00

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)