1.1 --- a/optimiser.py Tue Feb 28 22:08:02 2017 +0100
1.2 +++ b/optimiser.py Wed Mar 01 00:39:35 2017 +0100
1.3 @@ -49,12 +49,18 @@
1.4 self.attr_locations = None
1.5 self.all_attrnames = None
1.6
1.7 + self.existing_locations = []
1.8 + self.existing_attrnames = []
1.9 +
1.10 # Locations of parameters in parameter tables.
1.11
1.12 self.arg_locations = None
1.13 self.param_locations = None
1.14 self.all_paramnames = None
1.15
1.16 + self.existing_arg_locations = []
1.17 + self.existing_paramnames = []
1.18 +
1.19 # Specific attribute access information.
1.20
1.21 self.access_instructions = {}
1.22 @@ -78,6 +84,10 @@
1.23
1.24 # Optimiser activities.
1.25
1.26 + self.from_output()
1.27 +
1.28 + # Define or redefine structure information.
1.29 +
1.30 self.populate_objects()
1.31 self.position_attributes()
1.32 self.populate_parameters()
1.33 @@ -86,6 +96,45 @@
1.34 self.populate_constants()
1.35 self.initialise_access_instructions()
1.36
1.37 + def from_output(self):
1.38 +
1.39 + "Read input files that influence optimisation."
1.40 +
1.41 + self.read_locations("locations", self.existing_locations, self._line_to_list, list)
1.42 + self.read_locations("parameter_locations", self.existing_arg_locations, self._line_to_list, list)
1.43 + self.read_locations("attrnames", self.existing_attrnames, lambda x: x, lambda x: None)
1.44 + self.read_locations("paramnames", self.existing_paramnames, lambda x: x, lambda x: None)
1.45 +
1.46 + def _line_to_list(self, line):
1.47 +
1.48 + "Convert comma-separated values in 'line' to a list of values."
1.49 +
1.50 + return line.split(", ")
1.51 +
1.52 + def read_locations(self, filename, collection, decode, empty):
1.53 +
1.54 + """
1.55 + Read location details from 'filename' into 'collection', using 'decode'
1.56 + to convert each line and 'empty' to produce an empty result where no
1.57 + data is given on a line.
1.58 + """
1.59 +
1.60 + filename = join(self.output, filename)
1.61 +
1.62 + if exists(filename):
1.63 + f = open(filename)
1.64 + try:
1.65 + for line in f.readlines():
1.66 + line = line.rstrip()
1.67 + if line:
1.68 + attrnames = decode(line)
1.69 + else:
1.70 + attrnames = empty()
1.71 +
1.72 + collection.append(attrnames)
1.73 + finally:
1.74 + f.close()
1.75 +
1.76 def to_output(self):
1.77
1.78 "Write the output files using optimisation information."
1.79 @@ -309,26 +358,34 @@
1.80 self.all_attrs[(objkind, name)] = attrnames
1.81
1.82 self.locations = get_allocated_locations(self.all_attrs,
1.83 - get_attributes_and_sizes)
1.84 + get_attributes_and_sizes, self.existing_locations)
1.85
1.86 def populate_parameters(self):
1.87
1.88 "Populate parameter tables using parameter information."
1.89
1.90 + # Allocate positions from 1 onwards, ignoring the context argument.
1.91 +
1.92 self.arg_locations = [set()] + get_allocated_locations(
1.93 - self.importer.function_parameters, get_parameters_and_sizes)
1.94 + self.importer.function_parameters, get_parameters_and_sizes,
1.95 + self.existing_arg_locations[1:])
1.96
1.97 def position_attributes(self):
1.98
1.99 "Position specific attribute references."
1.100
1.101 - # Reverse the location mappings.
1.102 + # Reverse the location mappings, producing a mapping from attribute
1.103 + # names to positions.
1.104
1.105 attr_locations = self.attr_locations = {}
1.106 + self._position_attributes(attr_locations, self.locations)
1.107
1.108 - for i, attrnames in enumerate(self.locations):
1.109 - for attrname in attrnames:
1.110 - attr_locations[attrname] = i
1.111 + # Add any previously-known attribute locations. This prevents attributes
1.112 + # from being assigned different identifying codes by preserving obsolete
1.113 + # attribute codes.
1.114 +
1.115 + if self.existing_locations:
1.116 + self._position_attributes(attr_locations, self.existing_locations)
1.117
1.118 # Record the structures.
1.119
1.120 @@ -341,6 +398,18 @@
1.121 l.extend([None] * (position - len(l) + 1))
1.122 l[position] = attrname
1.123
1.124 + def _position_attributes(self, d, l):
1.125 +
1.126 + """
1.127 + For each attribute, store a mapping in 'd' to the index in 'l' at which
1.128 + it can be found.
1.129 + """
1.130 +
1.131 + for i, attrnames in enumerate(l):
1.132 + for attrname in attrnames:
1.133 + if not d.has_key(attrname):
1.134 + d[attrname] = i
1.135 +
1.136 def initialise_access_instructions(self):
1.137
1.138 "Expand access plans into instruction sequences."
1.139 @@ -650,16 +719,11 @@
1.140
1.141 "Position the parameters for each function's parameter table."
1.142
1.143 - # Reverse the location mappings.
1.144 + # Reverse the location mappings, producing a mapping from parameter
1.145 + # names to positions.
1.146
1.147 param_locations = self.param_locations = {}
1.148 -
1.149 - for i, argnames in enumerate(self.arg_locations):
1.150 -
1.151 - # Position the arguments.
1.152 -
1.153 - for argname in argnames:
1.154 - param_locations[argname] = i
1.155 + self._position_attributes(param_locations, self.arg_locations)
1.156
1.157 for name, argnames in self.importer.function_parameters.items():
1.158
1.159 @@ -690,7 +754,7 @@
1.160 these identifiers.
1.161 """
1.162
1.163 - self.all_attrnames, d = self._get_name_mapping(self.attr_locations)
1.164 + self.all_attrnames, d = self._get_name_mapping(self.attr_locations, self.existing_attrnames)
1.165
1.166 # Record the numbers indicating the locations of the names.
1.167
1.168 @@ -702,7 +766,7 @@
1.169 else:
1.170 l.append(d[attrname])
1.171
1.172 - self.all_paramnames, d = self._get_name_mapping(self.param_locations)
1.173 + self.all_paramnames, d = self._get_name_mapping(self.param_locations, self.existing_paramnames)
1.174
1.175 # Record the numbers indicating the locations of the names.
1.176
1.177 @@ -715,19 +779,42 @@
1.178 name, pos = value
1.179 l.append((d[name], pos))
1.180
1.181 - def _get_name_mapping(self, locations):
1.182 + def _get_name_mapping(self, locations, existing=None):
1.183
1.184 """
1.185 Get a sorted list of names from 'locations', then map them to
1.186 - identifying numbers. Return the list and the mapping.
1.187 + identifying numbers. Preserve the identifiers from the 'existing' list,
1.188 + if specified. Return the list and the mapping.
1.189 """
1.190
1.191 - all_names = locations.keys()
1.192 + d = {}
1.193 + l = []
1.194 +
1.195 + i = 0
1.196 + all_names = set(locations.keys())
1.197 +
1.198 + # Preserve the existing identifiers, if available.
1.199 +
1.200 + if existing:
1.201 + for name in existing:
1.202 + d[name] = i
1.203 + l.append(name)
1.204 + if name in all_names:
1.205 + all_names.remove(name)
1.206 + i += 1
1.207 +
1.208 + # Include all remaining names in order.
1.209 +
1.210 + all_names = list(all_names)
1.211 all_names.sort()
1.212 - d = {}
1.213 - for i, name in enumerate(all_names):
1.214 - d[name] = i
1.215 - return all_names, d
1.216 +
1.217 + for name in all_names:
1.218 + if not d.has_key(name):
1.219 + d[name] = i
1.220 + l.append(name)
1.221 + i += 1
1.222 +
1.223 + return l, d
1.224
1.225 def populate_constants(self):
1.226
1.227 @@ -911,7 +998,7 @@
1.228
1.229 return matrix, names, rsizes
1.230
1.231 -def get_allocated_locations(d, fn):
1.232 +def get_allocated_locations(d, fn, existing=None):
1.233
1.234 """
1.235 Return a list where each element corresponds to a structure location and
1.236 @@ -923,6 +1010,48 @@
1.237 matrix, names, rsizes = fn(d)
1.238 allocated = []
1.239
1.240 + # Verify any existing allocation.
1.241 +
1.242 + allocated_attrnames = set()
1.243 +
1.244 + if existing:
1.245 + for attrnames in existing:
1.246 +
1.247 + # Handle empty positions.
1.248 +
1.249 + if not attrnames:
1.250 + allocated.append([])
1.251 + continue
1.252 +
1.253 + base = None
1.254 +
1.255 + for attrname in attrnames:
1.256 +
1.257 + # Skip presumably-removed attribute names.
1.258 +
1.259 + if not matrix.has_key(attrname):
1.260 + continue
1.261 +
1.262 + # Handle the first attribute name.
1.263 +
1.264 + if base is None:
1.265 + base = matrix[attrname]
1.266 + allocated_attrnames.add(attrname)
1.267 + continue
1.268 +
1.269 + # Combine existing and new attribute positioning.
1.270 +
1.271 + new = combine_rows(base, matrix[attrname])
1.272 +
1.273 + if new:
1.274 + base = new
1.275 + allocated_attrnames.add(attrname)
1.276 + else:
1.277 + # NOTE: Signal an error or perform output reset.
1.278 + print "Attribute", attrname, "must be moved."
1.279 +
1.280 + allocated.append(base)
1.281 +
1.282 # Try to allocate each attribute name in turn.
1.283
1.284 pos = 0
1.285 @@ -930,6 +1059,12 @@
1.286 while pos < len(rsizes):
1.287 weight, size, free, attrname = rsizes[pos]
1.288
1.289 + # Ignore allocated attribute names.
1.290 +
1.291 + if attrname in allocated_attrnames:
1.292 + pos += 1
1.293 + continue
1.294 +
1.295 # Obtain the object information for the attribute name.
1.296
1.297 base = matrix[attrname]
1.298 @@ -942,6 +1077,12 @@
1.299 while y < len(rsizes):
1.300 _weight, _size, _free, _attrname = rsizes[y]
1.301
1.302 + # Ignore allocated attribute names.
1.303 +
1.304 + if _attrname in allocated_attrnames:
1.305 + y += 1
1.306 + continue
1.307 +
1.308 # Determine whether this attribute is supported by too many types
1.309 # to co-exist.
1.310
1.311 @@ -970,16 +1111,29 @@
1.312 allocated.append(base)
1.313 pos += 1
1.314
1.315 - # Return the list of attribute names from each row of the allocated
1.316 - # attributes table.
1.317 + return allocations_to_sets(allocated)
1.318 +
1.319 +def allocations_to_sets(allocated):
1.320 +
1.321 + """
1.322 + Return the list of attribute names from each row of the 'allocated'
1.323 + attributes table.
1.324 + """
1.325
1.326 locations = []
1.327 +
1.328 for attrnames in allocated:
1.329 l = set()
1.330 - for attrname in attrnames:
1.331 - if attrname:
1.332 - l.add(attrname)
1.333 +
1.334 + # Convert populated allocations.
1.335 +
1.336 + if attrnames:
1.337 + for attrname in attrnames:
1.338 + if attrname:
1.339 + l.add(attrname)
1.340 +
1.341 locations.append(l)
1.342 +
1.343 return locations
1.344
1.345 # vim: tabstop=4 expandtab shiftwidth=4