2.1 --- a/micropython/ast.py Fri Sep 26 23:48:05 2008 +0200
2.2 +++ b/micropython/ast.py Sun Sep 28 03:30:03 2008 +0200
2.3 @@ -19,349 +19,19 @@
2.4 this program. If not, see <http://www.gnu.org/licenses/>.
2.5 """
2.6
2.7 +from micropython.opt import Optimiser
2.8 from micropython.common import *
2.9 from micropython.data import *
2.10 from micropython.rsvp import *
2.11 import compiler.ast
2.12 from compiler.visitor import ASTVisitor
2.13
2.14 -class Optimiser:
2.15 -
2.16 - "A code optimiser."
2.17 -
2.18 - supported_optimisations = [
2.19 - "constant_storage", "known_target", "self_access",
2.20 - "temp_storage", "load_operations", "no_operations",
2.21 - "unused_results"
2.22 - ]
2.23 -
2.24 - def __init__(self, translation, optimisations=None):
2.25 -
2.26 - """
2.27 - Provide for the given 'translation' an optimiser for the desired
2.28 - 'optimisations'. See the 'supported_optimisations' attribute of this
2.29 - class for permitted values.
2.30 - """
2.31 -
2.32 - self.translation = translation
2.33 - self.optimisations = set(optimisations or [])
2.34 -
2.35 - # Optimisation tests.
2.36 -
2.37 - def should_optimise_constant_storage(self):
2.38 - return "constant_storage" in self.optimisations
2.39 -
2.40 - def should_optimise_known_target(self):
2.41 - return "known_target" in self.optimisations
2.42 -
2.43 - def should_optimise_self_access(self):
2.44 - return "self_access" in self.optimisations
2.45 -
2.46 - def should_optimise_temp_storage(self):
2.47 - return "temp_storage" in self.optimisations
2.48 -
2.49 - def should_optimise_load_operations(self):
2.50 - return "load_operations" in self.optimisations
2.51 -
2.52 - def should_optimise_away_no_operations(self):
2.53 - return "no_operations" in self.optimisations
2.54 -
2.55 - def should_optimise_unused_results(self):
2.56 - return "unused_results" in self.optimisations
2.57 -
2.58 - # Simple tests.
2.59 -
2.60 - def is_constant_input(self, instruction):
2.61 -
2.62 - "Return whether 'instruction' provides a constant input."
2.63 -
2.64 - return isinstance(instruction, LoadAddress) and instruction.attr.assignments == 1 or \
2.65 - isinstance(instruction, LoadConst)
2.66 -
2.67 - def is_constant_target(self, instruction):
2.68 -
2.69 - "Return whether 'instruction' provides a constant target."
2.70 -
2.71 - return isinstance(instruction, (StoreName, StoreAddress)) and \
2.72 - instruction.attr.assignments == 1
2.73 -
2.74 - def is_simple_input(self, instruction):
2.75 -
2.76 - """
2.77 - Return whether 'instruction' provides a simple input (typically a load
2.78 - instruction). A simple input is something which would be represented by
2.79 - a load operation from a CPU register or special memory location.
2.80 - """
2.81 -
2.82 - return isinstance(instruction, (LoadConst, LoadName, LoadTemp, LoadResult, LoadException, LoadAddress))
2.83 -
2.84 - def is_simple_input_user(self, instruction):
2.85 -
2.86 - """
2.87 - Return whether 'instruction' can use simple input from the current
2.88 - value. Such instructions would, in a low-level implementation, be able
2.89 - to have the simple input registers as operands.
2.90 - """
2.91 -
2.92 - return isinstance(instruction, (
2.93 - StoreTemp, StoreFrame, StoreResult, StoreException, # as the value being stored
2.94 - LoadAddressContext, LoadAttr, LoadAttrIndex, # as the object being referenced
2.95 - StoreAttr, StoreAttrIndex, StoreCallable, # as the object being referenced
2.96 - LoadCallable,
2.97 - TestIdentity, TestIdentityAddress, CheckSelf, # as one of the operands
2.98 - CheckException, CheckFrame, MakeObject,
2.99 - LoadContext # as the object providing the result
2.100 - ))
2.101 -
2.102 - def is_resultant_no_operation(self, instruction):
2.103 -
2.104 - """
2.105 - Return whether 'instruction' merely stores its input where the input
2.106 - originally came from.
2.107 - """
2.108 -
2.109 - return (
2.110 - isinstance(instruction.input, LoadTemp) and isinstance(instruction, StoreTemp) and
2.111 - instruction.input.attr == instruction.attr) or (
2.112 - isinstance(instruction.input, LoadResult) and isinstance(instruction, StoreResult)
2.113 - )
2.114 -
2.115 - def is_input(self, instruction):
2.116 -
2.117 - "Return whether 'instruction' provides an input."
2.118 -
2.119 - return isinstance(instruction, current_value_instructions)
2.120 -
2.121 - # Convenience tests on outputs.
2.122 -
2.123 - def have_constant_target(self):
2.124 -
2.125 - "Return whether the active instruction provides a constant target."
2.126 -
2.127 - return self.is_constant_target(self.translation.active)
2.128 -
2.129 - def have_constant_source(self):
2.130 -
2.131 - "Return whether the active instruction has a constant source."
2.132 -
2.133 - return self.is_constant_input(self.translation.active.source)
2.134 -
2.135 - # Convenience tests on inputs.
2.136 -
2.137 - def have_constant_input(self):
2.138 -
2.139 - "Return whether the active instruction provides a constant input."
2.140 -
2.141 - return self.is_constant_input(self.translation.active_value)
2.142 -
2.143 - have_known_target = have_constant_input
2.144 -
2.145 - def have_simple_input(self):
2.146 -
2.147 - "Return whether the active instruction provides a simple input."
2.148 -
2.149 - return self.is_simple_input(self.translation.active_value)
2.150 -
2.151 - def have_input(self):
2.152 -
2.153 - "Return whether the active instruction provides an input."
2.154 -
2.155 - return self.is_input(self.translation.active_value)
2.156 -
2.157 - def have_self_input(self):
2.158 -
2.159 - "Return whether the active instruction is a reference to self."
2.160 -
2.161 - return isinstance(self.translation.unit, Function) and \
2.162 - self.translation.unit.is_method() and isinstance(self.translation.active_value, LoadName) and \
2.163 - self.translation.active_value.attr.name == "self"
2.164 -
2.165 - def have_temp_compatible_access(self):
2.166 -
2.167 - """
2.168 - Indicate whether the active instruction can be used in place of access
2.169 - to a temporary variable retaining the result of the last instruction.
2.170 - """
2.171 -
2.172 - # LoadResult cannot be relied upon in general since the result register
2.173 - # could be updated since first being referenced.
2.174 -
2.175 - return isinstance(self.translation.active_value, (LoadName, LoadTemp, LoadAddress, LoadConst)) or \
2.176 - isinstance(self.translation.active_value, LoadResult) and self.translation.active_value is self.translation.active or \
2.177 - isinstance(self.translation.active_value, LoadException) and self.translation.active_value is self.translation.active
2.178 -
2.179 - def have_correct_self_for_target(self, context):
2.180 -
2.181 - "Return whether the 'context' is compatible with the current value."
2.182 -
2.183 - if context is not None and self.have_self_input():
2.184 -
2.185 - parent = self.translation.unit.parent
2.186 - if parent is context or parent.has_subclass(context) or context.has_subclass(parent):
2.187 - return 1
2.188 -
2.189 - return 0
2.190 -
2.191 - # Optimisation methods. See the supported_optimisations class attribute.
2.192 -
2.193 - def optimise_constant_storage(self):
2.194 -
2.195 - """
2.196 - Where the last operation stores a constant into a target which is also
2.197 - constant, optimise away both operations.
2.198 - """
2.199 -
2.200 - if self.should_optimise_constant_storage() and \
2.201 - self.have_constant_target() and \
2.202 - self.have_constant_source():
2.203 -
2.204 - self.translation.remove_op()
2.205 - return 1
2.206 - else:
2.207 - return 0
2.208 -
2.209 - def optimise_known_target(self):
2.210 -
2.211 - """
2.212 - Where the target of an invocation is known, provide information about it
2.213 - and its context. If a class is being invoked and the conditions are
2.214 - appropriate, get information about the specific initialiser.
2.215 - """
2.216 -
2.217 - if self.should_optimise_known_target() and self.have_known_target():
2.218 - last = self.translation.active_value
2.219 - target = last.attr.value
2.220 - context = last.attr.context
2.221 -
2.222 - return target, context
2.223 - else:
2.224 - return None
2.225 -
2.226 - def optimise_self_access(self, attrname, classes, node):
2.227 -
2.228 - """
2.229 - Where the provided 'attrname' accesses an attribute which occupies the
2.230 - same position in all possible objects which can be accessed, generate an
2.231 - instruction using one of the given 'classes', accessing the attribute
2.232 - directly.
2.233 - """
2.234 -
2.235 - AddressInstruction, AddressContextInstruction, AttrInstruction = classes
2.236 -
2.237 - if self.should_optimise_self_access() and self.have_self_input() and \
2.238 - not self.translation.unit.is_relocated(attrname):
2.239 -
2.240 - # Either generate an instruction operating on an instance attribute.
2.241 -
2.242 - try:
2.243 - attr = self.translation.unit.parent.instance_attributes()[attrname]
2.244 - self.translation.new_op(AttrInstruction(attr))
2.245 -
2.246 - # Or generate an instruction operating on a class attribute.
2.247 -
2.248 - except KeyError:
2.249 - attr = self.translation.unit.parent.all_attributes()[attrname]
2.250 -
2.251 - # Switch the context if the class attribute is compatible with
2.252 - # the instance.
2.253 -
2.254 - if attr.defined_within_hierarchy():
2.255 -
2.256 - # Only permit loading (not storing) of class attributes via self.
2.257 -
2.258 - if AddressContextInstruction is not None:
2.259 - self.translation.new_op(AddressContextInstruction(attr))
2.260 - else:
2.261 - raise TranslateError(self.translation.module.full_name(), node,
2.262 - "Storing of class attribute %r via self not permitted." % attrname)
2.263 -
2.264 - # Preserve the context if the class attribute comes from an
2.265 - # incompatible class.
2.266 -
2.267 - else:
2.268 - if AddressInstruction is not None:
2.269 - self.translation.new_op(AddressInstruction(attr))
2.270 - else:
2.271 - raise TranslateError(self.translation.module.full_name(), node,
2.272 - "Storing of class attribute %r via self not permitted." % attrname)
2.273 -
2.274 - return 1
2.275 - else:
2.276 - return 0
2.277 -
2.278 - def optimise_temp_storage(self):
2.279 -
2.280 - """
2.281 - Where the next operation would involve storing a value into temporary
2.282 - storage at 'temp_position', record and remove any simple instruction
2.283 - which produced the value to be stored such that instead of subsequently
2.284 - accessing the temporary storage, that instruction is substituted.
2.285 -
2.286 - If no optimisation can be achieved, a StoreTemp instruction is produced
2.287 - and the appropriate LoadTemp instruction is returned.
2.288 -
2.289 - Restriction: for use only in situations where the source of the
2.290 - temporary data will not be disturbed between its first access and its
2.291 - subsequent use.
2.292 - """
2.293 -
2.294 - if self.should_optimise_temp_storage() and \
2.295 - self.have_temp_compatible_access():
2.296 -
2.297 - # Remove the active value contributor.
2.298 -
2.299 - removed = self.translation.active
2.300 - self.translation.remove_active_value()
2.301 -
2.302 - # Extend the lifetime of any temporary storage location.
2.303 -
2.304 - self.translation.ensure_temp(removed)
2.305 - return removed
2.306 - else:
2.307 - return self.translation.get_temp()
2.308 -
2.309 - def optimise_load_operations(self, instruction):
2.310 -
2.311 - """
2.312 - Incorporate previous load operations into other operations.
2.313 - """
2.314 -
2.315 - if self.should_optimise_load_operations() and \
2.316 - self.have_simple_input() and \
2.317 - self.is_simple_input_user(instruction):
2.318 -
2.319 - self.translation.remove_active_value()
2.320 - instruction.input = self.translation.active_value
2.321 -
2.322 - def optimise_away_no_operations(self, instruction):
2.323 -
2.324 - """
2.325 - Optimise away operations which just store their inputs in the place
2.326 - the inputs originally came from.
2.327 - """
2.328 -
2.329 - if self.should_optimise_away_no_operations() and \
2.330 - self.is_resultant_no_operation(instruction):
2.331 -
2.332 - return 1
2.333 - else:
2.334 - return 0
2.335 -
2.336 - def optimise_unused_results(self):
2.337 -
2.338 - "Discard results which will not be used."
2.339 -
2.340 - if self.should_optimise_unused_results() and self.have_simple_input():
2.341 - self.translation.remove_active_value()
2.342 -
2.343 # Program visitors.
2.344
2.345 class Translation(ASTVisitor):
2.346
2.347 "A translated module."
2.348
2.349 - supported_optimisations = Optimiser.supported_optimisations
2.350 -
2.351 # Attribute access instructions, for use with the appropriate handlers.
2.352
2.353 attribute_load_instructions = (LoadAddress, LoadAddressContext, LoadAttr, LoadAttrIndex)
2.354 @@ -376,8 +46,7 @@
2.355
2.356 """
2.357 Initialise the translation with an inspected 'module', the 'importer'
2.358 - and optional 'optimisations'. See the 'supported_optimisations'
2.359 - attribute of this class for permitted values.
2.360 + and optional 'optimisations'.
2.361 """
2.362
2.363 ASTVisitor.__init__(self)
2.364 @@ -399,13 +68,6 @@
2.365
2.366 self.unit = None
2.367
2.368 - # The current "active" instruction.
2.369 - # As a rule, this will become the last instruction, but some
2.370 - # control-flow operations will flush the "active" instruction.
2.371 -
2.372 - self.active = None
2.373 - self.active_value = None
2.374 -
2.375 # The temporary storage used by the current assignment expression.
2.376
2.377 self.expr_temp = []
2.378 @@ -607,8 +269,7 @@
2.379 self.discard_temp(self.expr_temp.pop())
2.380
2.381 def set_source(self):
2.382 - if self.active is not None:
2.383 - self.active.source = self.expr_temp[-1]
2.384 + self.optimiser.set_source(self.expr_temp[-1])
2.385
2.386 # Optimise away constant storage if appropriate.
2.387
2.388 @@ -669,26 +330,14 @@
2.389 return
2.390
2.391 self.code.append(op)
2.392 - self.active = op
2.393 -
2.394 - # Record specific types of instructions for optimisation.
2.395 -
2.396 - if isinstance(op, current_value_instructions):
2.397 - self.active_value = op
2.398 + self.optimiser.set_new(op)
2.399
2.400 def remove_op(self):
2.401
2.402 "Remove the last instruction."
2.403
2.404 op = self.code.pop()
2.405 - self.active = None
2.406 -
2.407 - def remove_active_value(self):
2.408 -
2.409 - "Remove the value-providing active instruction if appropriate."
2.410 -
2.411 - if self.active_value is self.active:
2.412 - self.remove_op()
2.413 + self.optimiser.clear_active()
2.414
2.415 def replace_op(self, op):
2.416
2.417 @@ -703,7 +352,7 @@
2.418 Replace the value-providing active instruction with 'op' if appropriate.
2.419 """
2.420
2.421 - self.remove_active_value()
2.422 + self.optimiser.remove_active_value()
2.423 self.new_op(op)
2.424
2.425 def last_op(self):
2.426 @@ -715,13 +364,6 @@
2.427 except IndexError:
2.428 return None
2.429
2.430 - def clear_active(self):
2.431 -
2.432 - "Prevent incorrect optimisation."
2.433 -
2.434 - self.active = None
2.435 - self.active_value = None
2.436 -
2.437 # Visitor methods.
2.438
2.439 def default(self, node, *args):
2.440 @@ -754,7 +396,7 @@
2.441 # constant...
2.442
2.443 if self.optimiser.have_constant_input():
2.444 - last = self.active_value
2.445 + last = self.optimiser.active_value
2.446
2.447 # Get the details of the access.
2.448
2.449 @@ -1041,7 +683,7 @@
2.450
2.451 continue_label = self.new_label()
2.452 self.new_op(CheckSelf())
2.453 - self.active.source = temp
2.454 + self.optimiser.set_source(temp)
2.455 self.new_op(JumpIfTrue(continue_label))
2.456
2.457 # Where the context is inappropriate, drop the incomplete frame and
2.458 @@ -1301,7 +943,7 @@
2.459
2.460 # Prevent incorrect optimisation.
2.461
2.462 - self.clear_active()
2.463 + self.optimiser.reset()
2.464
2.465 self.set_label(end_label)
2.466
2.467 @@ -1386,7 +1028,7 @@
2.468
2.469 # Prevent incorrect optimisation.
2.470
2.471 - self.clear_active()
2.472 + self.optimiser.reset()
2.473
2.474 self.set_label(end_label)
2.475 return temp_out
2.476 @@ -1538,7 +1180,7 @@
2.477
2.478 # Prevent incorrect optimisation.
2.479
2.480 - self.clear_active()
2.481 + self.optimiser.reset()
2.482
2.483 self.new_op(temp)
2.484 self.discard_temp(temp)
2.485 @@ -2050,7 +1692,7 @@
2.486
2.487 # Prevent incorrect optimisation.
2.488
2.489 - self.clear_active()
2.490 + self.optimiser.reset()
2.491
2.492 def visitOr(self, node):
2.493 end_label = self.new_label()
2.494 @@ -2071,7 +1713,7 @@
2.495
2.496 # Prevent incorrect optimisation.
2.497
2.498 - self.clear_active()
2.499 + self.optimiser.reset()
2.500
2.501 self.new_op(temp)
2.502 self.discard_temp(temp)
2.503 @@ -2231,7 +1873,7 @@
2.504
2.505 # Prevent incorrect optimisation.
2.506
2.507 - self.clear_active()
2.508 + self.optimiser.reset()
2.509
2.510 def visitWith(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "With")
2.511
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/micropython/opt.py Sun Sep 28 03:30:03 2008 +0200
3.3 @@ -0,0 +1,420 @@
3.4 +#!/usr/bin/env python
3.5 +
3.6 +"""
3.7 +Optimise code produced by the AST translator.
3.8 +
3.9 +Copyright (C) 2007, 2008 Paul Boddie <paul@boddie.org.uk>
3.10 +
3.11 +This program is free software; you can redistribute it and/or modify it under
3.12 +the terms of the GNU General Public License as published by the Free Software
3.13 +Foundation; either version 3 of the License, or (at your option) any later
3.14 +version.
3.15 +
3.16 +This program is distributed in the hope that it will be useful, but WITHOUT
3.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
3.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
3.19 +details.
3.20 +
3.21 +You should have received a copy of the GNU General Public License along with
3.22 +this program. If not, see <http://www.gnu.org/licenses/>.
3.23 +"""
3.24 +
3.25 +from micropython.common import *
3.26 +from micropython.rsvp import *
3.27 +
3.28 +class Optimiser:
3.29 +
3.30 + "A code optimiser."
3.31 +
3.32 + supported_optimisations = [
3.33 + "constant_storage", "known_target", "self_access",
3.34 + "temp_storage", "load_operations", "no_operations",
3.35 + "unused_results"
3.36 + ]
3.37 +
3.38 + def __init__(self, translation, optimisations=None):
3.39 +
3.40 + """
3.41 + Provide for the given 'translation' an optimiser for the desired
3.42 + 'optimisations'. See the 'supported_optimisations' attribute of this
3.43 + class for permitted values.
3.44 + """
3.45 +
3.46 + self.translation = translation
3.47 + self.optimisations = set(optimisations or [])
3.48 +
3.49 + # The current "active" instruction.
3.50 + # As a rule, this will become the last instruction, but some
3.51 + # control-flow operations will flush the "active" instruction.
3.52 +
3.53 + self.active = None
3.54 +
3.55 + # The instruction providing the active value.
3.56 +
3.57 + self.active_value = None
3.58 +
3.59 + def reset(self):
3.60 +
3.61 + "Reset the optimiser, clearing the active instructions."
3.62 +
3.63 + self.clear_active_value()
3.64 + self.clear_active()
3.65 +
3.66 + def set_new(self, op):
3.67 +
3.68 + "Set the latest 'op' as the active instruction."
3.69 +
3.70 + self.set_active(op)
3.71 + self.set_active_value(op)
3.72 +
3.73 + def set_active_value(self, op):
3.74 +
3.75 + "Set the value-providing active instruction."
3.76 +
3.77 + if isinstance(op, current_value_instructions):
3.78 + self.active_value = op
3.79 +
3.80 + def clear_active_value(self):
3.81 +
3.82 + "Clear the value-providing active instruction."
3.83 +
3.84 + self.active_value = None
3.85 +
3.86 + def remove_active_value(self):
3.87 +
3.88 + "Remove the value-providing active instruction if appropriate."
3.89 +
3.90 + if self.active_value is self.active:
3.91 + removed = self.active
3.92 + self.translation.remove_op()
3.93 + return removed
3.94 + else:
3.95 + return None
3.96 +
3.97 + def set_source(self, expr):
3.98 +
3.99 + "Set the source of the active instruction to 'expr'."
3.100 +
3.101 + if self.active is not None:
3.102 + self.active.source = expr
3.103 +
3.104 + def set_active(self, op):
3.105 +
3.106 + "Set the active instruction."
3.107 +
3.108 + self.active = op
3.109 +
3.110 + def clear_active(self):
3.111 +
3.112 + "Clear the active instruction."
3.113 +
3.114 + self.active = None
3.115 +
3.116 + # Optimisation tests.
3.117 +
3.118 + def should_optimise_constant_storage(self):
3.119 + return "constant_storage" in self.optimisations
3.120 +
3.121 + def should_optimise_known_target(self):
3.122 + return "known_target" in self.optimisations
3.123 +
3.124 + def should_optimise_self_access(self):
3.125 + return "self_access" in self.optimisations
3.126 +
3.127 + def should_optimise_temp_storage(self):
3.128 + return "temp_storage" in self.optimisations
3.129 +
3.130 + def should_optimise_load_operations(self):
3.131 + return "load_operations" in self.optimisations
3.132 +
3.133 + def should_optimise_away_no_operations(self):
3.134 + return "no_operations" in self.optimisations
3.135 +
3.136 + def should_optimise_unused_results(self):
3.137 + return "unused_results" in self.optimisations
3.138 +
3.139 + # Simple tests.
3.140 +
3.141 + def is_constant_input(self, instruction):
3.142 +
3.143 + "Return whether 'instruction' provides a constant input."
3.144 +
3.145 + return isinstance(instruction, LoadAddress) and instruction.attr.assignments == 1 or \
3.146 + isinstance(instruction, LoadConst)
3.147 +
3.148 + def is_constant_target(self, instruction):
3.149 +
3.150 + "Return whether 'instruction' provides a constant target."
3.151 +
3.152 + return isinstance(instruction, (StoreName, StoreAddress)) and \
3.153 + instruction.attr.assignments == 1
3.154 +
3.155 + def is_simple_input(self, instruction):
3.156 +
3.157 + """
3.158 + Return whether 'instruction' provides a simple input (typically a load
3.159 + instruction). A simple input is something which would be represented by
3.160 + a load operation from a CPU register or special memory location.
3.161 + """
3.162 +
3.163 + return isinstance(instruction, (LoadConst, LoadName, LoadTemp, LoadResult, LoadException, LoadAddress))
3.164 +
3.165 + def is_simple_input_user(self, instruction):
3.166 +
3.167 + """
3.168 + Return whether 'instruction' can use simple input from the current
3.169 + value. Such instructions would, in a low-level implementation, be able
3.170 + to have the simple input registers as operands.
3.171 + """
3.172 +
3.173 + return isinstance(instruction, (
3.174 + StoreTemp, StoreFrame, StoreResult, StoreException, # as the value being stored
3.175 + LoadAddressContext, LoadAttr, LoadAttrIndex, # as the object being referenced
3.176 + StoreAttr, StoreAttrIndex, StoreCallable, # as the object being referenced
3.177 + LoadCallable,
3.178 + TestIdentity, TestIdentityAddress, CheckSelf, # as one of the operands
3.179 + CheckException, CheckFrame, MakeObject,
3.180 + LoadContext # as the object providing the result
3.181 + ))
3.182 +
3.183 + def is_resultant_no_operation(self, instruction):
3.184 +
3.185 + """
3.186 + Return whether 'instruction' merely stores its input where the input
3.187 + originally came from.
3.188 + """
3.189 +
3.190 + return (
3.191 + isinstance(instruction.input, LoadTemp) and isinstance(instruction, StoreTemp) and
3.192 + instruction.input.attr == instruction.attr) or (
3.193 + isinstance(instruction.input, LoadResult) and isinstance(instruction, StoreResult)
3.194 + )
3.195 +
3.196 + def is_input(self, instruction):
3.197 +
3.198 + "Return whether 'instruction' provides an input."
3.199 +
3.200 + return isinstance(instruction, current_value_instructions)
3.201 +
3.202 + # Convenience tests on outputs.
3.203 +
3.204 + def have_constant_target(self):
3.205 +
3.206 + "Return whether the active instruction provides a constant target."
3.207 +
3.208 + return self.is_constant_target(self.active)
3.209 +
3.210 + def have_constant_source(self):
3.211 +
3.212 + "Return whether the active instruction has a constant source."
3.213 +
3.214 + return self.is_constant_input(self.active.source)
3.215 +
3.216 + # Convenience tests on inputs.
3.217 +
3.218 + def have_constant_input(self):
3.219 +
3.220 + "Return whether the active instruction provides a constant input."
3.221 +
3.222 + return self.is_constant_input(self.active_value)
3.223 +
3.224 + have_known_target = have_constant_input
3.225 +
3.226 + def have_simple_input(self):
3.227 +
3.228 + "Return whether the active instruction provides a simple input."
3.229 +
3.230 + return self.is_simple_input(self.active_value)
3.231 +
3.232 + def have_input(self):
3.233 +
3.234 + "Return whether the active instruction provides an input."
3.235 +
3.236 + return self.is_input(self.active_value)
3.237 +
3.238 + def have_self_input(self):
3.239 +
3.240 + "Return whether the active instruction is a reference to self."
3.241 +
3.242 + return isinstance(self.translation.unit, Function) and \
3.243 + self.translation.unit.is_method() and isinstance(self.active_value, LoadName) and \
3.244 + self.active_value.attr.name == "self"
3.245 +
3.246 + def have_temp_compatible_access(self):
3.247 +
3.248 + """
3.249 + Indicate whether the active instruction can be used in place of access
3.250 + to a temporary variable retaining the result of the last instruction.
3.251 + """
3.252 +
3.253 + # LoadResult cannot be relied upon in general since the result register
3.254 + # could be updated since first being referenced.
3.255 +
3.256 + return isinstance(self.active_value, (LoadName, LoadTemp, LoadAddress, LoadConst)) or \
3.257 + isinstance(self.active_value, LoadResult) and self.active_value is self.active or \
3.258 + isinstance(self.active_value, LoadException) and self.active_value is self.active
3.259 +
3.260 + def have_correct_self_for_target(self, context):
3.261 +
3.262 + "Return whether the 'context' is compatible with the current value."
3.263 +
3.264 + if context is not None and self.have_self_input():
3.265 +
3.266 + parent = self.translation.unit.parent
3.267 + if parent is context or parent.has_subclass(context) or context.has_subclass(parent):
3.268 + return 1
3.269 +
3.270 + return 0
3.271 +
3.272 + # Optimisation methods. See the supported_optimisations class attribute.
3.273 +
3.274 + def optimise_constant_storage(self):
3.275 +
3.276 + """
3.277 + Where the last operation stores a constant into a target which is also
3.278 + constant, optimise away both operations.
3.279 + """
3.280 +
3.281 + if self.should_optimise_constant_storage() and \
3.282 + self.have_constant_target() and \
3.283 + self.have_constant_source():
3.284 +
3.285 + self.translation.remove_op()
3.286 + return 1
3.287 + else:
3.288 + return 0
3.289 +
3.290 + def optimise_known_target(self):
3.291 +
3.292 + """
3.293 + Where the target of an invocation is known, provide information about it
3.294 + and its context. If a class is being invoked and the conditions are
3.295 + appropriate, get information about the specific initialiser.
3.296 + """
3.297 +
3.298 + if self.should_optimise_known_target() and self.have_known_target():
3.299 + value = self.active_value
3.300 + target = value.attr.value
3.301 + context = value.attr.context
3.302 +
3.303 + return target, context
3.304 + else:
3.305 + return None
3.306 +
3.307 + def optimise_self_access(self, attrname, classes, node):
3.308 +
3.309 + """
3.310 + Where the provided 'attrname' accesses an attribute which occupies the
3.311 + same position in all possible objects which can be accessed, generate an
3.312 + instruction using one of the given 'classes', accessing the attribute
3.313 + directly.
3.314 + """
3.315 +
3.316 + AddressInstruction, AddressContextInstruction, AttrInstruction = classes
3.317 +
3.318 + if self.should_optimise_self_access() and self.have_self_input() and \
3.319 + not self.translation.unit.is_relocated(attrname):
3.320 +
3.321 + # Either generate an instruction operating on an instance attribute.
3.322 +
3.323 + try:
3.324 + attr = self.translation.unit.parent.instance_attributes()[attrname]
3.325 + self.translation.new_op(AttrInstruction(attr))
3.326 +
3.327 + # Or generate an instruction operating on a class attribute.
3.328 +
3.329 + except KeyError:
3.330 + attr = self.translation.unit.parent.all_attributes()[attrname]
3.331 +
3.332 + # Switch the context if the class attribute is compatible with
3.333 + # the instance.
3.334 +
3.335 + if attr.defined_within_hierarchy():
3.336 +
3.337 + # Only permit loading (not storing) of class attributes via self.
3.338 +
3.339 + if AddressContextInstruction is not None:
3.340 + self.translation.new_op(AddressContextInstruction(attr))
3.341 + else:
3.342 + raise TranslateError(self.translation.module.full_name(), node,
3.343 + "Storing of class attribute %r via self not permitted." % attrname)
3.344 +
3.345 + # Preserve the context if the class attribute comes from an
3.346 + # incompatible class.
3.347 +
3.348 + else:
3.349 + if AddressInstruction is not None:
3.350 + self.translation.new_op(AddressInstruction(attr))
3.351 + else:
3.352 + raise TranslateError(self.translation.module.full_name(), node,
3.353 + "Storing of class attribute %r via self not permitted." % attrname)
3.354 +
3.355 + return 1
3.356 + else:
3.357 + return 0
3.358 +
3.359 + def optimise_temp_storage(self):
3.360 +
3.361 + """
3.362 + Where the next operation would involve storing a value into temporary
3.363 + storage at 'temp_position', record and remove any simple instruction
3.364 + which produced the value to be stored such that instead of subsequently
3.365 + accessing the temporary storage, that instruction is substituted.
3.366 +
3.367 + If no optimisation can be achieved, a StoreTemp instruction is produced
3.368 + and the appropriate LoadTemp instruction is returned.
3.369 +
3.370 + Restriction: for use only in situations where the source of the
3.371 + temporary data will not be disturbed between its first access and its
3.372 + subsequent use.
3.373 + """
3.374 +
3.375 + if self.should_optimise_temp_storage() and \
3.376 + self.have_temp_compatible_access():
3.377 +
3.378 + # Remove the active value contributor.
3.379 +
3.380 + removed = self.remove_active_value()
3.381 +
3.382 + # Extend the lifetime of any temporary storage location.
3.383 +
3.384 + self.translation.ensure_temp(removed)
3.385 + return removed
3.386 + else:
3.387 + return self.translation.get_temp()
3.388 +
3.389 + def optimise_load_operations(self, instruction):
3.390 +
3.391 + """
3.392 + Incorporate previous load operations into other operations.
3.393 + """
3.394 +
3.395 + if self.should_optimise_load_operations() and \
3.396 + self.have_simple_input() and \
3.397 + self.is_simple_input_user(instruction):
3.398 +
3.399 + self.remove_active_value()
3.400 + instruction.input = self.active_value
3.401 +
3.402 + def optimise_away_no_operations(self, instruction):
3.403 +
3.404 + """
3.405 + Optimise away operations which just store their inputs in the place
3.406 + the inputs originally came from.
3.407 + """
3.408 +
3.409 + if self.should_optimise_away_no_operations() and \
3.410 + self.is_resultant_no_operation(instruction):
3.411 +
3.412 + return 1
3.413 + else:
3.414 + return 0
3.415 +
3.416 + def optimise_unused_results(self):
3.417 +
3.418 + "Discard results which will not be used."
3.419 +
3.420 + if self.should_optimise_unused_results() and self.have_simple_input():
3.421 + self.remove_active_value()
3.422 +
3.423 +# vim: tabstop=4 expandtab shiftwidth=4