274 lines
8.3 KiB
Python
274 lines
8.3 KiB
Python
import logging
|
|
|
|
from ScEpTIC import tools
|
|
from ScEpTIC.AST.elements.instructions.termination_instructions import ReturnOperation, BranchOperation, SwitchOperation
|
|
from ScEpTIC.AST.elements.value import Value
|
|
from ScEpTIC.exceptions import ParsingException, LLVMSyntaxErrorException
|
|
from ScEpTIC.llvmir_parser.instructions_parser import binary_operations
|
|
from ScEpTIC.llvmir_parser.sections_parser import global_vars
|
|
from ScEpTIC.llvmir_parser.token_generator import SWITCH_NEW_LINE_MARKER
|
|
|
|
|
|
def parse_terminator_instruction(text):
|
|
"""
|
|
Parses and returns a terminator instruction.
|
|
Terminator instructions:
|
|
- ret:
|
|
- br
|
|
- switch:
|
|
"""
|
|
|
|
logging.debug('Calling parse_terminator_instruction({})'.format(text))
|
|
|
|
instruction_type = None
|
|
|
|
if is_ret_instruction(text):
|
|
instruction_type = 'return'
|
|
instruction_value = parse_ret_instruction(text)
|
|
|
|
elif is_switch_instruction(text):
|
|
instruction_type = 'switch'
|
|
instruction_value = parse_switch_instruction(text)
|
|
|
|
elif is_branch_instruction(text):
|
|
instruction_type = 'branch'
|
|
instruction_value = parse_branch_instruction(text)
|
|
|
|
elif is_unreachable_instruction(text):
|
|
instruction_type = 'unreachable'
|
|
instruction_value = parse_unreachable_instruction(text)
|
|
|
|
if logging.getLogger().isEnabledFor(logging.DEBUG):
|
|
logging.debug('Parsed {} instruction:\n{}'.format(instruction_type, instruction_value))
|
|
|
|
if instruction_type is None:
|
|
raise ParsingException('Terminator instruction expected (ret, br or switch).\n{} given.'.format(text))
|
|
|
|
return instruction_value
|
|
|
|
|
|
def is_terminator_instruction(text):
|
|
"""
|
|
Returns if an instruction is a terminator instruction.
|
|
"""
|
|
|
|
logging.debug('Calling is_terminator_instruction({})'.format(text))
|
|
|
|
# not used in C
|
|
not_implemented = ['indirectbr', 'invoke', 'resume', 'catchswitch', 'catchret', 'cleanupret']
|
|
|
|
implemented = ['ret', 'br', 'switch', 'unreachable']
|
|
|
|
# in terminator operations, operation type is always in position 0
|
|
operation_type = text[0]
|
|
|
|
if operation_type in not_implemented:
|
|
raise NotImplementedError('Terminator Instruction "{}" not implemented!'.format(operation_type))
|
|
|
|
return operation_type in implemented
|
|
|
|
|
|
def is_ret_instruction(text):
|
|
"""
|
|
Returns if an instruction is a retturn.
|
|
"""
|
|
|
|
logging.debug('Calling is_ret_instruction({})'.format(text))
|
|
|
|
return text[0] == 'ret'
|
|
|
|
|
|
def is_unreachable_instruction(text):
|
|
"""
|
|
Returns if an instruction is an unreachable.
|
|
"""
|
|
|
|
logging.debug('Calling is_unreachable_instruction({})'.format(text))
|
|
|
|
return text[0] == 'unreachable'
|
|
|
|
|
|
def parse_ret_instruction(text):
|
|
"""
|
|
Parses and returns a ret instruction.
|
|
"""
|
|
|
|
logging.debug('Calling parse_ret_instruction({})'.format(text))
|
|
|
|
if not is_ret_instruction(text):
|
|
raise ParsingException('Terminator Instruction "ret" expected. {} given'.format(text))
|
|
|
|
text = text[1:]
|
|
|
|
if text[0] == 'void':
|
|
retval = Value('immediate', None, global_vars.parse_type('void', True, True))
|
|
else:
|
|
retval = binary_operations.parse_operand(text, True)
|
|
|
|
return ReturnOperation(retval)
|
|
|
|
|
|
def is_branch_instruction(text):
|
|
"""
|
|
Returns if an instruction is a branch.
|
|
"""
|
|
|
|
logging.debug('Calling is_br_instruction({})'.format(text))
|
|
|
|
return text[0] == 'br'
|
|
|
|
|
|
def parse_unreachable_instruction(text):
|
|
"""
|
|
Parses and returns an unreachable instruction.
|
|
"""
|
|
|
|
logging.debug('Calling parse_unreachable_instruction({})'.format(text))
|
|
|
|
return None
|
|
|
|
|
|
def parse_branch_instruction(text):
|
|
"""
|
|
Parses and returns a branch instruction.
|
|
"""
|
|
|
|
logging.debug('Calling parse_branch_instruction({})'.format(text))
|
|
|
|
if not is_branch_instruction(text):
|
|
raise ParsingException('Terminator Instruction "br" expected. {} given'.format(text))
|
|
|
|
if is_conditional_branch_instruction(text):
|
|
retval = parse_conditional_branch_instruction(text)
|
|
|
|
elif is_unconditional_branch_instruction(text):
|
|
retval = parse_unconditional_branch_instruction(text)
|
|
|
|
else:
|
|
raise ParsingException('Unable to identify branch operation: {}'.format(text))
|
|
|
|
return retval
|
|
|
|
|
|
def is_unconditional_branch_instruction(text):
|
|
"""
|
|
Returns if an instruction is an uncoditional branch.
|
|
"""
|
|
|
|
logging.debug('Calling is_unconditional_branch_instruction({})'.format(text))
|
|
|
|
return text[0] == 'br' and text[1] == 'label' and text[2] == '%'
|
|
|
|
|
|
def is_conditional_branch_instruction(text):
|
|
"""
|
|
Returns if an instruction is a conditional branch.
|
|
"""
|
|
|
|
logging.debug('Calling is_conditional_branch_instruction({})'.format(text))
|
|
|
|
return text[0] == 'br' and text[1] == 'i1'
|
|
|
|
|
|
def parse_unconditional_branch_instruction(text):
|
|
"""
|
|
Parses and returns an unconditional branch.
|
|
|
|
br label <target>
|
|
"""
|
|
|
|
logging.debug('Calling parse_unconditional_branch_instruction({})'.format(text))
|
|
|
|
if not is_unconditional_branch_instruction(text):
|
|
raise ParsingException('Unconditional Branch Instruction expected. {} given'.format(text))
|
|
|
|
if text[2] != '%':
|
|
raise LLVMSyntaxErrorException('Wrong label delimiter "{}". % expected.\n{}'.format(text[2], text))
|
|
|
|
# target on element 3
|
|
target = text[2]+text[3]
|
|
|
|
return BranchOperation(None, target, None)
|
|
|
|
|
|
def parse_conditional_branch_instruction(text):
|
|
"""
|
|
Parses and returns a conditional branch.
|
|
br i1 <cond>, label <iftrue>, label <iffalse>
|
|
"""
|
|
|
|
logging.debug('Calling parse_conditional_branch_instruction({})'.format(text))
|
|
|
|
if not is_conditional_branch_instruction(text):
|
|
raise ParsingException('Conditional Branch Instruction expected. {} given'.format(text))
|
|
|
|
if text[2] != '%':
|
|
raise LLVMSyntaxErrorException('Wrong condition delimiter "{}". % expected.\n{}'.format(text[2], text))
|
|
|
|
# condition on element 3
|
|
condition = binary_operations.parse_operand(text[2:4])
|
|
|
|
true_index = text.index('label')+1
|
|
true_label = tools.list_join(text[true_index:true_index+2])
|
|
|
|
false_index = text.index('label', true_index) + 1
|
|
false_label = tools.list_join(text[false_index:false_index+2])
|
|
|
|
return BranchOperation(condition, true_label, false_label)
|
|
|
|
|
|
def is_switch_instruction(text):
|
|
"""
|
|
Returns if an instruction is a switch
|
|
"""
|
|
|
|
logging.debug('Calling is_switch_instruction({})'.format(text))
|
|
|
|
return text[0] == 'switch' and '[' in text and ']' in text
|
|
|
|
|
|
def parse_switch_instruction(text):
|
|
"""
|
|
Parses and returns a switch instruction.
|
|
switch <intty> <value>, label <defaultdest> [ <intty> <val>, label <dest> ... ]
|
|
switch i32 %val, label %otherwise [ i32 0, label %onzero i32 1, label %onone i32 2, label %ontwo ]
|
|
"""
|
|
|
|
logging.debug('Calling parse_switch_instruction({})'.format(text))
|
|
|
|
if not is_switch_instruction(text):
|
|
raise ParsingException('Switch Instruction expected. {} given'.format(text))
|
|
|
|
# switch i32 %var,
|
|
# get type and element
|
|
first_comma = tools.get_index_in_context(text, ',')
|
|
element = binary_operations.parse_operand(text[1:first_comma], True)
|
|
|
|
text = text[first_comma+1:]
|
|
|
|
if text[0] != 'label':
|
|
raise LLVMSyntaxErrorException('Default label declaration in Switch Instruction expected. "{}" given.\n{}'.format(text[first_comma+1], text))
|
|
|
|
# get default label
|
|
default_label = text[1]+text[2]
|
|
|
|
# get switch values and split them into a list
|
|
switch_start, switch_end = tools.get_list_boundaries(text, '[', ']')
|
|
switch_elements = tools.split_into_sublist(text[switch_start:switch_end], SWITCH_NEW_LINE_MARKER)
|
|
|
|
switch_pairs = []
|
|
|
|
# switch pair example: i32 0, label %10
|
|
for switch_pair in switch_elements:
|
|
switch_pair = tools.split_into_sublist(switch_pair, ',')
|
|
|
|
value = binary_operations.parse_operand(switch_pair[0], True)
|
|
|
|
label = tools.list_join(switch_pair[1][1:])
|
|
|
|
value.label = label
|
|
|
|
switch_pairs.append(value)
|
|
|
|
return SwitchOperation(element, default_label, switch_pairs)
|