240 lines
7.9 KiB
Python
240 lines
7.9 KiB
Python
import logging
|
|
|
|
from ScEpTIC import tools
|
|
from ScEpTIC.AST.elements.instruction import Instruction
|
|
|
|
|
|
class ConversionOperation(Instruction):
|
|
"""
|
|
AST nodes for the LLVM Bitwise Instructions group
|
|
https://llvm.org/docs/LangRef.html#bitwiseops
|
|
"""
|
|
|
|
def __init__(self, conversion_type, operand, target_type, target):
|
|
super().__init__()
|
|
|
|
self.conversion_type = conversion_type
|
|
self.operand = operand
|
|
self.target_type = target_type
|
|
self.target = target
|
|
|
|
# instructions that do not correspond to any ISA instruction
|
|
if self.conversion_type in ['bitcast', 'addrspacecast', 'inttoptr', 'ptrtoint']:
|
|
self.tick_count = 0
|
|
|
|
|
|
def __str__(self):
|
|
retstr = super().__str__()
|
|
retstr += '{} {} to {}'.format(self.conversion_type, self.operand, self.target_type)
|
|
return retstr
|
|
|
|
|
|
def get_input_lookup(self):
|
|
"""
|
|
Returns the input lookup data for the current operation
|
|
"""
|
|
|
|
operand = self.operand.get_input_lookup()
|
|
|
|
return tools.merge_input_lookup_data(operand, tools.build_input_lookup_data(None, None))
|
|
|
|
|
|
def get_val(self):
|
|
"""
|
|
Returns the value obtained from the operation.
|
|
It converts address operands (if present) to relative spaces, applies the operation and converts them back to the absolute space.
|
|
"""
|
|
|
|
operand = self.operand.get_val()
|
|
|
|
prefix = None
|
|
|
|
# if operand is an address, get prefix and relative address (which can be used in conversion)
|
|
if self._vmstate.memory._is_absolute_address(operand):
|
|
prefix, operand = self._vmstate.memory._parse_absolute_address(operand)
|
|
|
|
value = self._get_val(operand)
|
|
|
|
# if prefix is set, value is an address. If so, convert it from relative to absolute space.
|
|
if prefix is not None:
|
|
value = self._vmstate.memory._convert_to_absolute_address(prefix, value)
|
|
|
|
logging.info('[{}] Executing {} on {} with result {}'.format(self.instruction_type, self.conversion_type, operand, value))
|
|
|
|
return value
|
|
|
|
|
|
def get_uses(self):
|
|
"""
|
|
Returns a list containing the names of the registers used by this instruction.
|
|
(used by register allocation)
|
|
"""
|
|
|
|
return self.operand.get_uses()
|
|
|
|
|
|
def replace_reg_name(self, old_reg_name, new_reg_name):
|
|
"""
|
|
Replaces the name of a register used by the instruction with a new one.
|
|
(used by register allocation)
|
|
"""
|
|
|
|
self.operand.replace_reg_name(old_reg_name, new_reg_name)
|
|
|
|
if self.target is not None:
|
|
self.target.replace_reg_name(old_reg_name, new_reg_name)
|
|
|
|
|
|
def _get_val(self, value):
|
|
"""
|
|
Returns the value returned from the operation, given its operands.
|
|
"""
|
|
|
|
if self.conversion_type == 'addrspacecast':
|
|
# is like bitcast, but for converting the address space
|
|
return value
|
|
|
|
elif self.conversion_type == 'fpext':
|
|
value = float(value)
|
|
|
|
# converts a fp value to a larger-sized one.
|
|
# for how data is represented in the simulator, it does nothing
|
|
return value
|
|
|
|
elif self.conversion_type == 'fptosi':
|
|
# fp to signed int conversion
|
|
value = float(value)
|
|
|
|
dim = len(self.target_type)
|
|
|
|
# convert to int value
|
|
value = int(round(value))
|
|
|
|
# apply max dimension
|
|
value = self.operand.convert_sint_to_sint(value, dim)
|
|
|
|
return value
|
|
|
|
elif self.conversion_type == 'fptoui':
|
|
# float to unsigned int conversion
|
|
value = float(value)
|
|
|
|
dim = len(self.target_type)
|
|
|
|
# convert to int value
|
|
value = int(round(value))
|
|
|
|
# apply max dimension
|
|
value = self.operand.convert_sint_to_uint(value, dim)
|
|
|
|
# NB: elements in registers / memory are saved as SIGNED int.
|
|
# Operations that uses UNSIGNED operands, first converts the SIGNED operand to UNSIGNED one.
|
|
# (SIGNED VS UNSIGNED is just a mode of interpretation)
|
|
|
|
return value
|
|
|
|
elif self.conversion_type == 'fptrunc':
|
|
# converts a fp value to a smaller-sized one.
|
|
# for how data is represented in the simulator, it does nothing
|
|
value = float(value)
|
|
|
|
return value
|
|
|
|
elif self.conversion_type == 'bitcast':
|
|
# bitcast does not apply any conversion.
|
|
# it just tells the compiler to treat data as it was already written as "target_type"
|
|
return value
|
|
|
|
elif self.conversion_type == 'inttoptr':
|
|
# returns back the integer as memory location (address)
|
|
# for how addresses are represented, it does nothing.
|
|
|
|
return value
|
|
|
|
elif self.conversion_type == 'ptrtoint':
|
|
# returns the memory address as an integer
|
|
# for how addresses are represented, it does nothing.
|
|
|
|
return value
|
|
|
|
elif self.conversion_type == 'sext':
|
|
value = int(value)
|
|
|
|
initial_dim = len(self.operand)
|
|
value = self.operand.convert_sint_to_bin(value, initial_dim)
|
|
|
|
target_dim = len(self.target_type)
|
|
prefix = value[0] * (target_dim - initial_dim)
|
|
|
|
value = prefix + value
|
|
|
|
return self.operand.convert_bin_to_sint(value)
|
|
|
|
elif self.conversion_type == 'sitofp':
|
|
# convert an integer to a float
|
|
return float(value)
|
|
|
|
elif self.conversion_type == 'uitofp':
|
|
# convert an unsigned integer to a float
|
|
|
|
# convert memory cell value to unsigned int.
|
|
value = int(value)
|
|
|
|
value = self.operand.convert_sint_to_uint(value, len(self.operand))
|
|
|
|
return float(value)
|
|
|
|
elif self.conversion_type == 'trunc':
|
|
dim = len(self.operand)
|
|
value = self.operand.convert_sint_to_bin(int(value), dim)
|
|
|
|
trunc = dim-len(self.target_type)
|
|
|
|
value = self.operand.convert_bin_to_sint(value[trunc:])
|
|
|
|
return value
|
|
|
|
elif self.conversion_type == 'zext':
|
|
initial_dim = len(self.operand)
|
|
|
|
value = self.operand.convert_sint_to_bin(value, initial_dim)
|
|
|
|
dim = len(self.target_type)
|
|
|
|
value = '0' * (dim - initial_dim) + value
|
|
|
|
# for how data is represented, zero expansion does nothing
|
|
return self.operand.convert_bin_to_sint(value)
|
|
|
|
# if gets there, the conversion mode is not supported.
|
|
raise NotImplementedError('{} is not supported for now!'.format(self.conversion_type))
|
|
|
|
|
|
def resolve_memory_tag(self, elements):
|
|
"""
|
|
Resolves and returns the memory tag of the targeted element
|
|
"""
|
|
if self.memory_tag is None:
|
|
self.memory_tag = self.operand.resolve_memory_tag(elements)
|
|
|
|
return self.memory_tag
|
|
|
|
def resolve_memory_tag_dependency(self, elements):
|
|
if self.memory_tag_dependency is None:
|
|
self.memory_tag_dependency = self.operand.resolve_memory_tag_dependency(elements)
|
|
|
|
return self.memory_tag_dependency
|
|
|
|
def resolve_memory_address_chain(self, elements):
|
|
"""
|
|
Returns a list of all the instructions required to get the address of the targeted element(s)
|
|
"""
|
|
chain = []
|
|
|
|
if self.target is not None:
|
|
chain.append(self)
|
|
|
|
chain.append(self.operand.resolve_memory_address_chain(elements))
|
|
|
|
return chain
|