# HG changeset patch # User Paul Boddie # Date 1488545072 -3600 # Node ID b5e52aaecf41713719472d698547cbaf452bc43f # Parent d1bf04436c7c078b676259f04fb291ca38daf931 Added support for explicitly specifying attribute and parameter information in order to direct allocation activities, introducing tests of output attribute/ parameter code assignments against existing assignments. diff -r d1bf04436c7c -r b5e52aaecf41 docs/lplc.1 --- a/docs/lplc.1 Fri Mar 03 13:41:13 2017 +0100 +++ b/docs/lplc.1 Fri Mar 03 13:44:32 2017 +0100 @@ -78,6 +78,22 @@ Show invocations where a callable may be involved that cannot accept the arguments provided .PP +Control over program organisation can be exercised using the following options +with each requiring an input filename providing a particular form of +information: +.TP +.B \-\-attr\-codes +Attribute codes identifying named object attributes +.TP +.B \-\-attr\-locations +Attribute locations in objects +.TP +.B \-\-param\-codes +Parameter codes identifying named parameters +.TP +.B \-\-param\-locations +Parameter locations in signatures +.PP The following informational options can be specified to produce output instead of compiling a program: .PP @@ -98,6 +114,28 @@ compiled. Otherwise, where structures or signatures change in a way that is incompatible with already-compiled code, the entire program will be generated and compiled again. +.PP +The +.BR \-r " and " \-\-reset +options force inspection and compilation to occur again but will still attempt +to preserve structure and signature information. Meanwhile, the +.BR \-R " and " \-\-reset\-all +options remove all traces of previous program information, requiring that all +such information be generated again. +.SH PROGRAM CONFIGURATION +Use of the +.BR \-\-attr\-codes " and " \-\-param\-codes +options is intended to allow common catalogues of identifying codes to be +maintained. Similarly, use of the +.BR \-\-attr\-locations " and " \-\-param\-locations +options is intended to allow common representations to be maintained. +.PP +Beyond incremental compilation, these features would allow already-compiled +programs and libraries to exchange information in a compatible way, although +this is not yet supported in any significant way. However, the +.B \-\-attr\-locations +option can be useful in directing the attribute allocation process and +potentially making program representations more efficient. .SH EXAMPLES Compile the main program in .BR hello.py , diff -r d1bf04436c7c -r b5e52aaecf41 lplc --- a/lplc Fri Mar 03 13:41:13 2017 +0100 +++ b/lplc Fri Mar 03 13:44:32 2017 +0100 @@ -82,6 +82,12 @@ else: return l, needed +def getvalue(l, i): + if l and len(l) > i: + return l[i] + else: + return None + def remove_all(dirname): "Remove 'dirname' and its contents." @@ -145,6 +151,15 @@ args Show invocations where a callable may be involved that cannot accept the arguments provided +Control over program organisation can be exercised using the following options +with each requiring an input filename providing a particular form of +information: + +--attr-codes Attribute codes identifying named object attributes +--attr-locations Attribute locations in objects +--param-codes Parameter codes identifying named parameters +--param-locations Parameter locations in signatures + The following informational options can be specified to produce output instead of compiling a program: @@ -171,11 +186,16 @@ # Determine the options and arguments. + attrnames = [] + attrlocations = [] debug = False gc_sections = False ignore_env = False make = True make_verbose = True + outputs = [] + paramnames = [] + paramlocations = [] reset = False reset_all = False timings = True @@ -185,7 +205,6 @@ unrecognised = [] filenames = [] - outputs = [] # Obtain program filenames by default. @@ -193,10 +212,15 @@ needed = None for arg in args: - if arg in ("-c", "--compile"): make = False + if arg == "--attr-codes": l = attrnames; needed = 1 + elif arg == "--attr-locations": l = attrlocations; needed = 1 + elif arg in ("-c", "--compile"): make = False elif arg in ("-E", "--no-env"): ignore_env = True elif arg in ("-g", "--debug"): debug = True elif arg in ("-G", "--gc-sections"): gc_sections = True + # "P" handled below. + elif arg == "--param-codes": l = paramnames; needed = 1 + elif arg == "--param-locations": l = paramlocations; needed = 1 elif arg in ("-q", "--quiet"): make_verbose = False elif arg in ("-r", "--reset"): reset = True elif arg in ("-R", "--reset-all"): reset_all = True @@ -290,18 +314,20 @@ if timings: now = stopwatch("Deduction", now) - o = optimiser.Optimiser(i, d, output_dir) + o = optimiser.Optimiser(i, d, output_dir, + getvalue(attrnames, 0), getvalue(attrlocations, 0), + getvalue(paramnames, 0), getvalue(paramlocations, 0)) o.to_output() + if timings: now = stopwatch("Optimisation", now) + # Detect structure or signature changes demanding a reset of the # generated sources. reset = reset or o.need_reset() - if timings: now = stopwatch("Optimisation", now) - g = generator.Generator(i, o, generated_dir) - g.to_output(debug, gc_sections) + g.to_output(reset, debug, gc_sections) if timings: now = stopwatch("Generation", now) diff -r d1bf04436c7c -r b5e52aaecf41 optimiser.py --- a/optimiser.py Fri Mar 03 13:41:13 2017 +0100 +++ b/optimiser.py Fri Mar 03 13:44:32 2017 +0100 @@ -31,18 +31,32 @@ "Optimise objects in a program." - def __init__(self, importer, deducer, output): + def __init__(self, importer, deducer, output, + attrnames_filename=None, locations_filename=None, + paramnames_filename=None, parameter_locations_filename=None): """ Initialise an instance using the given 'importer' and 'deducer' that will perform the arrangement of attributes for program objects, writing the results to the given 'output' directory. + + If 'attrnames_filename', 'locations_filename', 'paramnames_filename', or + 'parameter_locations_filename' are given, they will be used to + explicitly indicate existing attribute code, attribute position, + parameter code, and parameter position information respectively. """ self.importer = importer self.deducer = deducer self.output = output + # Explicitly-specified attribute and parameter sources. + + self.attrnames_filename = attrnames_filename + self.locations_filename = locations_filename + self.paramnames_filename = paramnames_filename + self.parameter_locations_filename = parameter_locations_filename + # Detection of differences between any existing structure or signature # information and the generated information. @@ -60,6 +74,7 @@ self.all_attrnames = None self.existing_attrnames = None + self.indicated_attrnames = None # Locations of parameters in parameter tables. @@ -72,6 +87,7 @@ self.all_paramnames = None self.existing_paramnames = None + self.indicated_paramnames = None # Specific attribute access information. @@ -127,20 +143,49 @@ self.check_output() - # Existing attribute and parameter positioning information. + # Existing attribute and parameter positioning information. This + # influences the positions of attributes and parameters found in the + # program. + + locations_filename = self.locations_filename or \ + join(self.output, "locations") + + parameter_locations_filename = self.parameter_locations_filename or \ + join(self.output, "parameter_locations") - self.existing_locations = self.read_locations("locations", self._line_to_list, list) - self.existing_arg_locations = self.read_locations("parameter_locations", self._line_to_list, list) + self.existing_locations = self.read_data(locations_filename, self._line_to_list, list) + self.existing_arg_locations = self.read_data(parameter_locations_filename, self._line_to_list, list) - # Existing attribute and parameter code information. + # Existing attribute and parameter code information. This is used to + # check the compatibility of the output against any assignments + # previously made. + + identity = lambda x: x + none = lambda x: None - self.existing_attrnames = self.read_locations("attrnames", lambda x: x, lambda x: None) - self.existing_paramnames = self.read_locations("paramnames", lambda x: x, lambda x: None) + attrnames_filename = join(self.output, "attrnames") + paramnames_filename = join(self.output, "paramnames") + + self.existing_attrnames = self.read_data(attrnames_filename, identity, none) + self.existing_paramnames = self.read_data(paramnames_filename, identity, none) + + # Explicitly-specified attribute name and parameter name codes. These + # direct assignment of codes in the program. + + self.indicated_attrnames = self.attrnames_filename and \ + self.read_data(self.attrnames_filename, identity, none) - # Existing structure and signature information. + self.indicated_paramnames = self.paramnames_filename and \ + self.read_data(self.paramnames_filename, identity, none) + + # Existing structure and signature information. This is used to check + # the output and detect whether structures or signatures have changed. - self.existing_structures = dict(self.read_locations("structures", self._line_to_structure_pairs, list)) - self.existing_parameters = dict(self.read_locations("parameters", self._line_to_signature_pairs, list)) + structures_filename = join(self.output, "structures") + parameters_filename = join(self.output, "parameters") + + self.existing_structures = dict(self.read_data(structures_filename, self._line_to_structure_pairs, list)) + self.existing_parameters = dict(self.read_data(parameters_filename, self._line_to_signature_pairs, list)) def _line_to_list(self, line): @@ -171,7 +216,7 @@ values = map(lambda x: x != '-' and x or None, line.split(", ")) return (decode_reference(ref), values) - def read_locations(self, filename, decode, empty): + def read_data(self, filename, decode, empty): """ Read location details from 'filename', using 'decode' to convert each @@ -179,7 +224,6 @@ line, returning a collection. """ - filename = join(self.output, filename) collection = [] if exists(filename): @@ -839,7 +883,13 @@ these identifiers. """ - self.all_attrnames, d = self._get_name_mapping(self.attr_locations, self.existing_attrnames) + # Initialise the mapping from attribute names to codes. + + l = self.all_attrnames = []; d = {} + self._init_name_mapping(l, d, self.existing_attrnames) + if self.indicated_attrnames: + self._init_name_mapping(l, d, self.indicated_attrnames) + self._update_name_mapping(l, d, self.attr_locations) # Record the numbers indicating the locations of the names. @@ -851,7 +901,13 @@ else: l.append(d[attrname]) - self.all_paramnames, d = self._get_name_mapping(self.param_locations, self.existing_paramnames) + # Initialise the mapping from parameter names to codes. + + l = self.all_paramnames = []; d = {} + self._init_name_mapping(l, d, self.existing_paramnames) + if self.indicated_paramnames: + self._init_name_mapping(l, d, self.indicated_paramnames) + self._update_name_mapping(l, d, self.param_locations) # Record the numbers indicating the locations of the names. @@ -864,43 +920,57 @@ name, pos = value l.append((d[name], pos)) - def _get_name_mapping(self, locations, existing=None): + def _init_name_mapping(self, l, d, existing): """ - Get a sorted list of names from 'locations', then map them to - identifying numbers. Preserve the identifiers from the 'existing' list, - if specified. Return the list and the mapping. + Initialise the name collection 'l', with mapping 'd', using the + 'existing' mapping. """ - d = {} - l = [] - i = 0 - all_names = set(locations.keys()) + + for name in existing: + + # Test for the name in another position. + + if d.has_key(name): + if d[name] != i: + raise OptimiseError, "Name %s has conflicting codes: %d and %d." % \ + (name, d[name], i) + else: - # Preserve the existing identifiers, if available. + # Test for other usage of the position. - if existing: - for name in existing: + if i < len(l): + if l[i] != name: + raise OptimiseError, "Position %d has conflicting names: %s and %s." % \ + (i, name, d[name]) + l[i] = name + else: + l.append(name) + d[name] = i - l.append(name) - if name in all_names: - all_names.remove(name) - i += 1 + + i += 1 + + def _update_name_mapping(self, l, d, locations): - # Include all remaining names in order. + """ + Using any existing identifiers supplied by 'l' and 'd', update the + identifiers using a sorted list of names from 'locations'. + """ - all_names = list(all_names) + all_names = list(locations.keys()) all_names.sort() + i = len(l) + for name in all_names: if not d.has_key(name): d[name] = i l.append(name) i += 1 - return l, d - def populate_constants(self): """