303 lines
8.5 KiB
Python
303 lines
8.5 KiB
Python
import logging
|
|
|
|
from ScEpTIC import tools
|
|
from ScEpTIC.AST.elements.metadata import Metadata
|
|
from ScEpTIC.exceptions import ParsingException, LLVMSyntaxErrorException
|
|
from ScEpTIC.llvmir_parser.token_generator import TOKEN_SPACE
|
|
|
|
|
|
def parse_metadata_section(text, log_section_content = False):
|
|
"""
|
|
Parses and returns the metadata section, using parse_metadata.
|
|
!name = [distinct] !(...)
|
|
"""
|
|
|
|
logging.debug('Calling parse_metadata_section({}, {})'.format(text, log_section_content))
|
|
|
|
for line in text:
|
|
parse_metadata(line)
|
|
|
|
metadatas = Metadata.elements
|
|
|
|
if logging.getLogger().isEnabledFor(logging.INFO):
|
|
log_str = 'Parsed metadata section. {} metadata found'.format(len(metadatas))
|
|
|
|
if log_section_content:
|
|
log_str += ':\n{}'.format(tools.fancy_dict_to_str(metadatas))
|
|
|
|
else:
|
|
log_str += '.\n{}'.format(tools.fancy_dict_keys_to_str(metadatas))
|
|
|
|
logging.info(log_str)
|
|
|
|
return metadatas
|
|
|
|
|
|
def parse_metadata(text):
|
|
"""
|
|
Parses and returns a line containing metadata information.
|
|
"""
|
|
|
|
logging.debug('Calling parse_metadata({})'.format(text))
|
|
|
|
if not is_metadata(text):
|
|
raise ParsingException('Metadata must start with "!". {} given.'.format(text))
|
|
|
|
name = text[0]+text[1]
|
|
|
|
retval = parse_metadata_body(text, name)
|
|
|
|
if logging.getLogger().isEnabledFor(logging.DEBUG):
|
|
log_vals = " {}".format(retval)
|
|
logging.debug('Parsed metadata {}:\n{}'.format(name, tools.instert_new_line_every(log_vals, new_line_padding = ' ')))
|
|
|
|
return retval
|
|
|
|
|
|
def get_metadata_right_side(text):
|
|
"""
|
|
Remove useless tokens from metadata.
|
|
"""
|
|
|
|
logging.debug('Calling extract_metadata_start({})'.format(text))
|
|
|
|
try:
|
|
index = text.index('!')
|
|
|
|
except ValueError:
|
|
raise LLVMSyntaxErrorException('Metadata right side must contain "!". {} given.'.format(text))
|
|
|
|
return text[index+1:]
|
|
|
|
|
|
def get_metadata_body(text):
|
|
"""
|
|
Returns the metadata body (text after = ).
|
|
"""
|
|
|
|
logging.debug('Calling get_metadata_body({})'.format(text))
|
|
|
|
try:
|
|
start = text.index('=')
|
|
|
|
except ValueError:
|
|
raise ParsingException('Unable to parse given metadata: {}'.format(text))
|
|
|
|
if text[0] != '!':
|
|
raise ParsingException('LLVM IR syntax erorr! Metadata body must start with "!". {} given.'.format(text[0]))
|
|
|
|
return get_metadata_right_side(text[start+1:])
|
|
|
|
|
|
def parse_metadata_body(text, metadata_name):
|
|
"""
|
|
Parses and returns the body of a metadata.
|
|
It returns a Metadata object.
|
|
"""
|
|
|
|
logging.debug('Calling parse_metadata_body({}, {})'.format(text, metadata_name))
|
|
|
|
body = get_metadata_body(text)
|
|
|
|
# verify metadata type and parse accordingly
|
|
if body[0] == '{':
|
|
# collection
|
|
return parse_metadata_collection(body, metadata_name)
|
|
|
|
elif body[1] == '(':
|
|
# metadata function
|
|
return parse_metadata_function(body, metadata_name)
|
|
|
|
else:
|
|
raise NotImplementedError(body)
|
|
|
|
|
|
def parse_metadata_collection(text, metadata_name, raise_exception = True):
|
|
"""
|
|
Parses a metadata collection and returns it.
|
|
!id = {!elem (, !elem)*}
|
|
"""
|
|
|
|
logging.debug('Calling parse_metadata_collection({}, {}, {})'.format(text, metadata_name, raise_exception))
|
|
|
|
# find delimiters for elements
|
|
try:
|
|
start, end = tools.get_list_boundaries(text, '{', '}')
|
|
|
|
except ValueError:
|
|
if raise_exception:
|
|
raise ParsingException('Metadata collection expected. {} given.'.format(text))
|
|
|
|
else:
|
|
return False
|
|
|
|
# remove ! and "
|
|
content = text[start:end]
|
|
|
|
# get actual elements of the metadata by splitting the list with ,
|
|
content = tools.split_into_sublist(content, ',')
|
|
|
|
includes = []
|
|
values = []
|
|
|
|
for element in content:
|
|
# metadata starts with ! and a number. e.g. !1
|
|
if element[0] == '!' and element[1].isdigit():
|
|
element_str = tools.list_to_string(element)
|
|
includes.append(element_str)
|
|
|
|
# skip to next element
|
|
continue
|
|
|
|
try:
|
|
val = tools.list_to_string_using_boundaries(element, '"', continue_on_exception=True, replace_elements={TOKEN_SPACE: ' '})
|
|
values.append(val)
|
|
|
|
except ValueError:
|
|
values.append(element)
|
|
|
|
data = Metadata(metadata_name, 'collection', 'collection', values, includes)
|
|
|
|
return data
|
|
|
|
|
|
def parse_metadata_function(text, metadata_name):
|
|
"""
|
|
Parses all type of metadata functions and returns the parsed metadata function.
|
|
"""
|
|
|
|
logging.debug('Calling parse_metadata_function({}, {})'.format(text, metadata_name))
|
|
|
|
# get list of arguments (name, value)
|
|
content = get_metadata_body_arg_list(text)
|
|
|
|
values = {}
|
|
|
|
for element in content:
|
|
# name: value
|
|
name = element[0]
|
|
val = element[2:]
|
|
|
|
# check if includes other metadata
|
|
if val[0] == '!':
|
|
collection = parse_metadata_collection(val, None, False)
|
|
|
|
if collection:
|
|
val = collection
|
|
|
|
else:
|
|
val = tools.list_to_string(val, {TOKEN_SPACE: ' '})
|
|
|
|
else:
|
|
# fetch actual value
|
|
val = tools.list_to_string_using_boundaries(val, '"', continue_on_exception=True, replace_elements={TOKEN_SPACE: ' '})
|
|
|
|
values[name] = val
|
|
|
|
# text[0] contains the name of the metadata function
|
|
|
|
data = Metadata(metadata_name, 'function', text[0], values, [])
|
|
|
|
return data
|
|
|
|
|
|
def get_metadata_body_arg_list(text, do_split = True):
|
|
"""
|
|
Returns the argument list of a metadata "function".
|
|
"""
|
|
|
|
logging.debug('Calling get_metadata_body_arg_list({}, {})'.format(text, do_split))
|
|
|
|
# find delimiters for elements
|
|
try:
|
|
start, end = tools.get_list_boundaries(text, '(', ')', right_most_end_token=True)
|
|
|
|
except ValueError:
|
|
raise LLVMSyntaxErrorException('Unable to find delimiters for metadata argument list. {}'.format(text))
|
|
|
|
content = text[start:end]
|
|
|
|
# get a list of elements-values
|
|
if do_split:
|
|
content = tools.split_context_into_sublist(content, ',')
|
|
|
|
return content
|
|
|
|
|
|
def is_metadata(text):
|
|
"""
|
|
Returns if parsed line represents metadata.
|
|
"""
|
|
|
|
logging.debug('Calling is_metadata({})'.format(text))
|
|
|
|
return isinstance(text, list) and len(text) > 1 and text[0] == '!'
|
|
|
|
|
|
def get_element_metadata(text, elem_name, return_index = False):
|
|
"""
|
|
Returns the metadata associated to other instructions a line of llvm code.
|
|
"""
|
|
|
|
logging.debug('Calling get_element_metadata({}, {}, {})'.format(text, elem_name, return_index))
|
|
|
|
closed_par = [']', ')']
|
|
|
|
metadata_abs_index = 0
|
|
|
|
# get only metadata outside arguments
|
|
for par in closed_par:
|
|
|
|
try:
|
|
metadata_base_index = tools.list_rindex(text, par) + 1
|
|
metadata_abs_index += metadata_base_index
|
|
text = text[metadata_base_index:]
|
|
|
|
except ValueError:
|
|
pass
|
|
|
|
# !name !N
|
|
metadata = {}
|
|
metadata_start_index = 0
|
|
token = '!'
|
|
offset = 0
|
|
|
|
while token in text[offset:]:
|
|
# get index of metadata name
|
|
index = text.index(token, offset) + 1
|
|
|
|
if metadata_start_index == 0:
|
|
# , ! name ! value
|
|
# I need the index of the comma, which is index of first ! -2
|
|
metadata_start_index = index - 2
|
|
|
|
if text[index+1] != token:
|
|
raise LLVMSyntaxErrorException('Invalid metadata for {}.'.format(elem_name))
|
|
|
|
name = text[index]
|
|
value = text[index+1]+text[index+2]
|
|
|
|
if name in metadata:
|
|
metadata[name].append(value)
|
|
else:
|
|
metadata[name] = [value]
|
|
|
|
offset = index + 3
|
|
|
|
if len(metadata) == 0:
|
|
logging.warning('No metadata information present for {}.'.format(elem_name))
|
|
metadata_start_index = 0
|
|
metadata_abs_index = 0
|
|
|
|
# only debug metadata relevant for my application
|
|
if 'dbg' in metadata:
|
|
metadata = Metadata(None, 'collection', 'collection', [], metadata['dbg'])
|
|
|
|
else:
|
|
metadata = None
|
|
|
|
if return_index:
|
|
return metadata, (metadata_start_index + metadata_abs_index)
|
|
|
|
return metadata
|