# HG changeset patch # User Paul Boddie # Date 1218998809 -7200 # Node ID 42c644df055c403960e86efed73af5976e4a6446 # Parent f3612fdd334b22e89159fe60a921cbef1da50f83 Introduced the active_value attribute in order to track instructions which affect the current value, thus making optimisations slightly more transparent. Introduced the elimination of context checking where the target and context are known. diff -r f3612fdd334b -r 42c644df055c micropython/ast.py --- a/micropython/ast.py Sun Aug 17 02:58:28 2008 +0200 +++ b/micropython/ast.py Sun Aug 17 20:46:49 2008 +0200 @@ -35,11 +35,21 @@ "constant_storage", "known_target", "self_access", "temp_storage", "load_operations", "no_operations", "unused_results" ] + # Attribute access instructions, for use with the appropriate handlers. + attribute_load_instructions = (LoadAddress, LoadAddressContext, LoadAttr, LoadAttrIndex) attribute_store_instructions = (None, None, StoreAttr, StoreAttrIndex) + + # Name access instructions, for use with the appropriate handlers. + name_load_instructions = (LoadName, LoadAddress) name_store_instructions = (StoreName, StoreAddress) + # Instructions which affect the current value. + + current_value_instructions = (LoadConst, LoadName, LoadTemp, LoadAddress, LoadAddressContext, LoadAttr, LoadAttrIndex, + LoadCallable, LoadContext, LoadResult, LoadException, LoadBoolean, MakeObject) + def __init__(self, module, importer, optimisations=None): """ @@ -72,6 +82,7 @@ # control-flow operations will flush the "active" instruction. self.active = None + self.active_value = None # The temporary storage used by the current assignment expression. @@ -257,6 +268,11 @@ self.code.append(op) self.active = op + # Record specific types of instructions for optimisation. + + if isinstance(op, self.current_value_instructions): + self.active_value = op + def remove_op(self): "Remove the last instruction." @@ -333,7 +349,7 @@ StoreTemp, StoreFrame, StoreResult, StoreException, # as the value being stored LoadAddressContext, LoadAttr, LoadAttrIndex, # as the object being referenced StoreAttr, StoreAttrIndex, StoreCallable, # as the object being referenced - TestIdentity # as one of the operands + TestIdentity, CheckSelf # as one of the operands )) def _is_resultant_no_operation(self, instruction): @@ -353,17 +369,9 @@ "Return whether 'instruction' provides an input." - return isinstance(instruction, (LoadConst, LoadName, LoadTemp, LoadResult, LoadBoolean) + self.attribute_load_instructions) - - # Convenience tests. - - def _have_constant_input(self): - - "Return whether the active instruction provides a constant input." - - return self._is_constant_input(self.active) - - _have_known_target = _have_constant_input + return isinstance(instruction, self.current_value_instructions) + + # Convenience tests on outputs. def _have_constant_target(self): @@ -377,25 +385,35 @@ return self._is_constant_input(self.active.source) + # Convenience tests on inputs. + + def _have_constant_input(self): + + "Return whether the active instruction provides a constant input." + + return self._is_constant_input(self.active_value) + + _have_known_target = _have_constant_input + def _have_simple_input(self): "Return whether the active instruction provides a simple input." - return self._is_simple_input(self.active) + return self._is_simple_input(self.active_value) def _have_input(self): "Return whether the active instruction provides an input." - return self._is_input(self.active) + return self._is_input(self.active_value) def _have_self_input(self): "Return whether the active instruction is a reference to self." return isinstance(self.unit, Function) and \ - self.unit.is_method() and isinstance(self.active, LoadName) and \ - self.active.attr.name == "self" + self.unit.is_method() and isinstance(self.active_value, LoadName) and \ + self.active_value.attr.name == "self" def _have_temp_compatible_access(self): @@ -407,7 +425,19 @@ # LoadResult cannot be relied upon, since in general the result register # could be updated since first being referenced. - return isinstance(self.active, (LoadName, LoadTemp, LoadAddress, LoadConst)) + return isinstance(self.active_value, (LoadName, LoadTemp, LoadAddress, LoadConst)) + + def _have_correct_self_for_target(self, context): + + "Return whether the 'context' is compatible with the current value." + + if context is not None and self._have_self_input(): + + parent = self.unit.parent + if parent is context or parent.has_subclass(context) or context.has_subclass(parent): + return 1 + + return 0 # Optimisation methods. See the supported_optimisations class attribute. @@ -540,9 +570,10 @@ self._have_simple_input() and \ self._is_simple_input_user(instruction): - last = self.last_op() - self.remove_op() - instruction.input = last + if self.active_value is self.active: + self.remove_op() + + instruction.input = self.active_value def _optimise_away_no_operations(self, instruction): @@ -596,7 +627,7 @@ # Where the last operation (defining the attribute owner) yields a # constant... - if self._have_constant_input(): + if self._is_constant_input(self.active): last = self.active # Get the details of the access. @@ -846,20 +877,25 @@ # the target where methods are being invoked via classes). if first and expect_context: - continue_label = self.new_label() - self.new_op(CheckSelf()) - self.new_op(JumpIfTrue(continue_label)) - - # Where the context is inappropriate, drop the incomplete frame and - # raise an exception. - - self.new_op(DropFrame()) - self.new_op(LoadResult()) - - self.load_builtin("TypeError", node) - self.new_op(StoreException()) - self.new_op(RaiseException()) - self.set_label(continue_label) + + # Drop any test if the target and the context are known. + + if not self._have_correct_self_for_target(context): + + continue_label = self.new_label() + self.new_op(CheckSelf()) + self.new_op(JumpIfTrue(continue_label)) + + # Where the context is inappropriate, drop the incomplete frame and + # raise an exception. + + self.new_op(DropFrame()) + self.new_op(LoadResult()) + + self.load_builtin("TypeError", node) + self.new_op(StoreException()) + self.new_op(RaiseException()) + self.set_label(continue_label) first = 0 frame_pos += 1 @@ -1275,6 +1311,7 @@ # Prevent incorrect optimisation. self.active = None + self.active_value = None def visitAssert(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "Assert") @@ -1688,6 +1725,7 @@ # Prevent incorrect optimisation. self.active = None + self.active_value = None def visitOr(self, node): next_label = self.new_label() @@ -1703,6 +1741,7 @@ # Prevent incorrect optimisation. self.active = None + self.active_value = None def visitPass(self, node): pass @@ -1850,6 +1889,7 @@ # Prevent incorrect optimisation. self.active = None + self.active_value = None def visitWith(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "With") diff -r f3612fdd334b -r 42c644df055c micropython/data.py --- a/micropython/data.py Sun Aug 17 02:58:28 2008 +0200 +++ b/micropython/data.py Sun Aug 17 20:46:49 2008 +0200 @@ -280,8 +280,8 @@ return isinstance(self.parent, Class) and isinstance(self.context, Class) and ( self.context is self.parent or - self.context in self.parent.descendants or - self.parent in self.context.descendants) + self.context.has_subclass(self.parent) or + self.parent.has_subclass(self.context)) def __repr__(self): return "Attr(%r, %s, %s, %r, %s, %r)" % ( @@ -450,6 +450,9 @@ for base in self.bases: base.add_descendant(cls) + def has_subclass(self, other): + return other in self.descendants + "Return the attribute names provided by this class only." class_attribute_names = NamespaceDict.keys diff -r f3612fdd334b -r 42c644df055c tests/subclass.py --- a/tests/subclass.py Sun Aug 17 02:58:28 2008 +0200 +++ b/tests/subclass.py Sun Aug 17 20:46:49 2008 +0200 @@ -13,7 +13,7 @@ class C(A, B): def a(self): - pass + A.a(self) def b(self): pass