1 #!/usr/bin/env python 2 3 """ 4 Optimise code produced by the AST translator. 5 6 Copyright (C) 2007, 2008, 2009 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from micropython.common import * 23 from micropython.data import * 24 from micropython.rsvp import * 25 26 class Optimiser: 27 28 "A code optimiser." 29 30 supported_optimisations = [ 31 # Code generation optimisations: 32 "constant_storage", "constant_accessor", "known_target", "self_access", 33 "temp_storage", "load_operations", "no_operations", "unused_results", 34 "unused_handlers", "accesses_by_usage", 35 # Inspection optimisations: 36 "unused_objects" 37 ] 38 39 def __init__(self, translation, optimisations=None): 40 41 """ 42 Provide for the given 'translation' an optimiser for the desired 43 'optimisations'. See the 'supported_optimisations' attribute of this 44 class for permitted values. 45 """ 46 47 self.translation = translation 48 self.optimisations = set(optimisations or []) 49 50 # The current "active" instruction. 51 # As a rule, this will become the last instruction, but some 52 # control-flow operations will flush the "active" instruction. 53 54 self.active = None 55 56 # The instruction providing the active value. 57 58 self.active_value = None 59 60 def reset(self): 61 62 "Reset the optimiser, clearing the active instructions." 63 64 self.clear_active_value() 65 self.clear_active() 66 67 def set_new(self, op): 68 69 "Set the latest 'op' as the active instruction." 70 71 self.set_active(op) 72 self.set_active_value(op) 73 74 def set_active_value(self, op): 75 76 "Set the value-providing active instruction." 77 78 if isinstance(op, current_value_instructions): 79 self.active_value = op 80 81 def clear_active_value(self): 82 83 "Clear the value-providing active instruction." 84 85 self.active_value = None 86 87 def remove_active_value(self): 88 89 """ 90 Remove the value-providing active instruction from the generated code, 91 if appropriate, but keep a record of the active instruction itself. 92 """ 93 94 if self.active_value is self.active: 95 removed = self.active 96 self.translation.remove_op() 97 return removed 98 else: 99 return None 100 101 def set_source(self, expr): 102 103 "Set the source of the active instruction to 'expr'." 104 105 if self.active is not None: 106 self.active.source = expr 107 108 def set_active(self, op): 109 110 "Set the active instruction." 111 112 self.active = op 113 114 def clear_active(self): 115 116 "Clear the active instruction." 117 118 self.active = None 119 120 # Optimisation tests. 121 122 def should_optimise_constant_storage(self): 123 return "constant_storage" in self.optimisations 124 125 def should_optimise_constant_accessor(self): 126 return "constant_accessor" in self.optimisations 127 128 def should_optimise_known_target(self): 129 return "known_target" in self.optimisations 130 131 def should_optimise_self_access(self): 132 return "self_access" in self.optimisations 133 134 def should_optimise_temp_storage(self): 135 return "temp_storage" in self.optimisations 136 137 def should_optimise_load_operations(self): 138 return "load_operations" in self.optimisations 139 140 def should_optimise_away_no_operations(self): 141 return "no_operations" in self.optimisations 142 143 def should_optimise_unused_results(self): 144 return "unused_results" in self.optimisations 145 146 def should_optimise_unused_handlers(self): 147 return "unused_handlers" in self.optimisations 148 149 def should_optimise_accesses_by_attribute_usage(self): 150 return "accesses_by_usage" in self.optimisations 151 152 # Simple tests. 153 154 def is_constant_input(self, instruction): 155 156 "Return whether 'instruction' provides a constant input." 157 158 return isinstance(instruction, LoadAddress) and instruction.attr.assignments == 1 and \ 159 isinstance(instruction.attr.get_value(), Constant) or \ 160 isinstance(instruction, (LoadConst, LoadClass, LoadFunction)) 161 162 def is_constant_target(self, instruction): 163 164 "Return whether 'instruction' provides a constant target." 165 166 # NOTE: Removed StoreName, since this would then demand population of 167 # NOTE: locals/frames. 168 169 return isinstance(instruction, (StoreAddress, StoreAddressContext)) and \ 170 instruction.attr.assignments == 1 171 172 def is_simple_input(self, instruction): 173 174 """ 175 Return whether 'instruction' provides a simple input (typically a load 176 instruction). A simple input is something which would be represented by 177 a load operation from a CPU register or special memory location. 178 """ 179 180 return isinstance(instruction, (LoadConst, LoadClass, LoadFunction, LoadName, LoadTemp, LoadResult, LoadException, LoadAddress)) 181 182 def is_simple_input_user(self, instruction): 183 184 """ 185 Return whether 'instruction' can use simple input from the current 186 value. Such instructions would, in a low-level implementation, be able 187 to have the simple input registers as operands. 188 """ 189 190 return isinstance(instruction, simple_input_user_instructions) 191 192 def is_resultant_no_operation(self, instruction): 193 194 """ 195 Return whether 'instruction' merely stores its input where the input 196 originally came from. 197 """ 198 199 return ( 200 isinstance(instruction.input, LoadTemp) and isinstance(instruction, StoreTemp) and 201 instruction.input.attr == instruction.attr) or ( 202 isinstance(instruction.input, LoadResult) and isinstance(instruction, StoreResult) 203 ) 204 205 def is_input(self, instruction): 206 207 "Return whether 'instruction' provides an input." 208 209 return isinstance(instruction, current_value_instructions) 210 211 # Convenience tests on outputs. 212 213 def have_constant_target(self): 214 215 "Return whether the active instruction provides a constant target." 216 217 return self.is_constant_target(self.active) 218 219 def have_constant_source(self): 220 221 "Return whether the active instruction has a constant source." 222 223 return self.is_constant_input(self.active.source) 224 225 # Convenience tests on inputs. 226 227 def have_constant_input(self): 228 229 "Return whether the active instruction provides a constant input." 230 231 return self.is_constant_input(self.active_value) 232 233 have_known_target = have_constant_input 234 235 def have_simple_input(self): 236 237 "Return whether the active instruction provides a simple input." 238 239 return self.is_simple_input(self.active_value) 240 241 def have_input(self): 242 243 "Return whether the active instruction provides an input." 244 245 return self.is_input(self.active_value) 246 247 def have_self_input(self, unit): 248 249 """ 250 Return whether the active instruction is a reference to self in the 251 given 'unit'. 252 """ 253 254 return isinstance(unit, Function) and \ 255 unit.is_method() and isinstance(self.active_value, LoadName) and \ 256 self.active_value.attr.name == "self" 257 258 def have_temp_compatible_access(self): 259 260 """ 261 Indicate whether the active instruction can be used in place of access 262 to a temporary variable retaining the result of the last instruction. 263 """ 264 265 # LoadResult cannot be relied upon in general since the result register 266 # could be updated since first being referenced. 267 268 return isinstance(self.active_value, (LoadName, LoadTemp, LoadAddress, LoadConst, LoadClass, LoadFunction)) or \ 269 isinstance(self.active_value, LoadResult) and self.active_value is self.active or \ 270 isinstance(self.active_value, LoadException) and self.active_value is self.active 271 272 def have_correct_self_for_target(self, context, unit): 273 274 "Return whether the 'context' is compatible with the given 'unit'." 275 276 if context is not None and self.have_self_input(unit): 277 278 parent = unit.parent 279 if parent is context or parent.has_subclass(context) or context.has_subclass(parent): 280 return 1 281 282 return 0 283 284 def have_empty_handler(self, instruction): 285 286 """ 287 Return whether the active instruction defined a handler for exceptions 288 which is then discarded by the given 'instruction'. 289 """ 290 291 return isinstance(self.translation.last_op(), PushHandler) and isinstance(instruction, PopHandler) 292 293 # Optimisation methods. See the supported_optimisations class attribute. 294 295 def optimise_constant_storage(self): 296 297 """ 298 Where the last operation stores a constant into a target which is also 299 constant, indicate that both operations should be optimised away. 300 """ 301 302 return self.should_optimise_constant_storage() and \ 303 self.have_constant_target() and \ 304 self.have_constant_source() 305 306 def optimise_constant_accessor(self): 307 308 """ 309 Where the object whose attribute is being accessed is constant, provide 310 information about its full name. 311 """ 312 313 if self.should_optimise_constant_accessor() and self.have_constant_input(): 314 value = self.active_value 315 316 # Get the details of the access. 317 318 if isinstance(value.attr, Const): 319 target_name = value.attr.value_type_name() 320 else: 321 target = value.attr.get_value() 322 323 if target is None: 324 return None # no clearly defined target 325 elif isinstance(target, Const): 326 return target.value_type_name() 327 elif isinstance(target, Instance): 328 return None # skip production of optimised code 329 else: 330 return target.full_name() 331 332 else: 333 return None 334 335 def optimise_known_target(self): 336 337 """ 338 Where the target of an invocation is known, provide information about it 339 and its context. If a class is being invoked and the conditions are 340 appropriate, get information about the specific initialiser. 341 """ 342 343 if self.should_optimise_known_target() and self.have_known_target(): 344 value = self.active_value 345 target = value.attr.get_value() 346 context = value.attr.get_context() 347 348 # Return target details only if this is clearly defined. 349 350 if target is not None: 351 return target, context 352 353 return None 354 355 def optimise_self_access(self, unit, attrname): 356 357 """ 358 Return whether code in the given 'unit' is able to access the given 359 'attrname' through the same position in all possible objects which might 360 be accessed. 361 """ 362 363 return self.should_optimise_self_access() and \ 364 self.have_self_input(unit) and not unit.is_relocated(attrname) 365 366 def optimise_temp_storage(self): 367 368 """ 369 Where the next operation would involve storing a value into temporary 370 storage at 'temp_position', record and remove any simple instruction 371 which produced the value to be stored such that instead of subsequently 372 accessing the temporary storage, that instruction is substituted. 373 374 If no optimisation can be achieved, a StoreTemp instruction is produced 375 and the appropriate LoadTemp instruction is returned. 376 377 Restriction: for use only in situations where the source of the 378 temporary data will not be disturbed between its first access and its 379 subsequent use. 380 """ 381 382 if self.should_optimise_temp_storage() and \ 383 self.have_temp_compatible_access(): 384 385 # Remove the active value contributor. 386 387 removed = self.remove_active_value() 388 389 # Extend the lifetime of any temporary storage location. 390 391 self.translation.ensure_temp(removed) 392 return removed 393 else: 394 return self.translation.get_temp() 395 396 def optimise_load_operations(self, instruction): 397 398 """ 399 Incorporate previous load operations into other operations. 400 """ 401 402 if self.should_optimise_load_operations() and \ 403 self.have_simple_input() and \ 404 self.is_simple_input_user(instruction): 405 406 self.remove_active_value() 407 instruction.input = self.active_value 408 409 def optimise_away_no_operations(self, instruction): 410 411 """ 412 Optimise away operations which just store their inputs in the place 413 the inputs originally came from. 414 """ 415 416 return self.should_optimise_away_no_operations() and \ 417 self.is_resultant_no_operation(instruction) 418 419 def optimise_unused_results(self): 420 421 "Discard results which will not be used." 422 423 if self.should_optimise_unused_results() and self.have_simple_input(): 424 self.remove_active_value() 425 426 def optimise_unused_handlers(self, instruction): 427 428 "Discard handlers which will not be used." 429 430 if self.should_optimise_unused_handlers() and self.have_empty_handler(instruction): 431 self.translation.remove_op() 432 return 1 433 else: 434 return 0 435 436 # vim: tabstop=4 expandtab shiftwidth=4