# HG changeset patch # User Paul Boddie # Date 1216495607 -7200 # Node ID 3bf7d0e10c17985a78c1e725a729d17ff4a26f12 # Parent bc2c5bb980a8be78f9011f7189d9cbfe3e8619c3 Made methods to handle assignment expression values. Changed constant storage optimisations to consider sources. Introduced distinctions between some storage operations which use the current value (StoreTemp, StoreFrame, StoreResult) and others which use a source defined in a full assignment operation. Made class attribute assignment via self forbidden (removing StoreAddressContext). Added an unused results optimisation. Fixed attribute_load_instructions references. Improved instruction string representations to show source details where no inputs are defined. Made a failure version of the attributes test. diff -r bc2c5bb980a8 -r 3bf7d0e10c17 micropython/ast.py --- a/micropython/ast.py Sat Jul 19 00:39:50 2008 +0200 +++ b/micropython/ast.py Sat Jul 19 21:26:47 2008 +0200 @@ -31,10 +31,10 @@ "A translated module." - supported_optimisations = ["constant_storage", "known_target", "self_access", "temp_storage", "load_operations"] + supported_optimisations = ["constant_storage", "known_target", "self_access", "temp_storage", "load_operations", "unused_results"] attribute_load_instructions = (LoadAddress, LoadAddressContext, LoadAttr, LoadAttrIndex) - attribute_store_instructions = (StoreAddress, StoreAddressContext, StoreAttr, StoreAttrIndex) + attribute_store_instructions = (StoreAddress, None, StoreAttr, StoreAttrIndex) def __init__(self, module, importer, optimisations=None): @@ -188,6 +188,25 @@ def drop_exception_labels(self): self.exception_labels.pop() + # Assignment expression values. + + def record_value(self): + self.expr_temp = self._optimise_temp_storage() + + def discard_value(self): + self.discard_temp(self.expr_temp) + self.expr_temp = None + + def set_source(self): + if self.active is not None: + self.active.source = self.expr_temp + + # Optimise away constant storage if appropriate. + + self._optimise_constant_storage() + + # Temporary storage administration. + def get_temp(self): """ @@ -214,12 +233,6 @@ "Add 'op' to the generated code." - # Optimise away constant storage if appropriate. - # The target and value loading operations are also removed. - - if self._optimise_constant_storage(op): - return - # Optimise load operations employed by this instruction. self._optimise_load_operations(op) @@ -267,6 +280,9 @@ def _should_optimise_load_operations(self): return "load_operations" in self.optimisations + def _should_optimise_unused_results(self): + return "unused_results" in self.optimisations + # Simple tests. def _is_constant_input(self, instruction): @@ -287,14 +303,23 @@ "Return whether 'instruction' provides a simple input." - return isinstance(instruction, (LoadName, LoadTemp, LoadResult, LoadAddress)) + return isinstance(instruction, (LoadConst, LoadName, LoadTemp, LoadResult, LoadAddress)) def _is_simple_input_user(self, instruction): - "Return whether 'instruction' can use simple input." + "Return whether 'instruction' can use simple input from the current value." - return isinstance(instruction, - (StoreName, StoreTemp, StoreFrame, StoreAddress, RaiseException) + self.attribute_store_instructions) + return isinstance(instruction, ( + StoreTemp, StoreFrame, StoreResult, RaiseException, # as the value being stored + LoadAddressContext, LoadAttr, LoadAttrIndex, # as the object being referenced + StoreAttr, StoreAttrIndex # as the object being referenced + )) + + def _is_input(self, instruction): + + "Return whether 'instruction' provides an input." + + return isinstance(instruction, (LoadConst, LoadName, LoadTemp, LoadResult) + self.attribute_load_instructions) # Convenience tests. @@ -306,12 +331,30 @@ _have_known_target = _have_constant_input + def _have_constant_target(self): + + "Return whether the active instruction provides a constant target." + + return self._is_constant_target(self.active) + + def _have_constant_source(self): + + "Return whether the active instruction has a constant source." + + return self._is_constant_input(self.active.source) + def _have_simple_input(self): - "Return whether the active instruction provides a local input." + "Return whether the active instruction provides a simple input." return self._is_simple_input(self.active) + def _have_input(self): + + "Return whether the active instruction provides an input." + + return self._is_input(self.active) + def _have_self_input(self): "Return whether the active instruction is a reference to self." @@ -331,16 +374,16 @@ # Optimisation methods. See the supported_optimisations class attribute. - def _optimise_constant_storage(self, instruction): + def _optimise_constant_storage(self): """ - Where this operation should store a constant into a target which is - also constant, optimise away both operations. + Where the last operation stores a constant into a target which is also + constant, optimise away both operations. """ if self._should_optimise_constant_storage() and \ - self._is_constant_target(instruction) and \ - self._have_constant_input(): + self._have_constant_target() and \ + self._have_constant_source(): self.remove_op() return 1 @@ -373,7 +416,7 @@ else: return None - def _optimise_self_access(self, attrname, classes): + def _optimise_self_access(self, attrname, classes, node): """ Where the provided 'attrname' accesses an attribute which occupies the @@ -398,7 +441,14 @@ except KeyError: attr = self.unit.parent.all_attributes()[attrname] new_attr = attr.via_instance() - self.new_op(AddressContextInstruction(new_attr)) + + # Only permit loading (not storing) of class attributes via self. + + if AddressContextInstruction is not None: + self.new_op(AddressContextInstruction(new_attr)) + else: + raise TranslateError(self.module.full_name(), node, + "Storing of class attribute %r via self not permitted." % attrname) return 1 else: @@ -443,6 +493,13 @@ self.remove_op() instruction.input = last + def _optimise_unused_results(self): + + "Discard results which will not be used." + + if self._have_input(): + self.remove_op() + # Visitor methods. def default(self, node, *args): @@ -518,7 +575,7 @@ # see if the attribute is acceptably positioned and produce a direct # access to the attribute. - elif self._optimise_self_access(attrname, (AddressInstruction, AddressContextInstruction, AttrInstruction)): + elif self._optimise_self_access(attrname, (AddressInstruction, AddressContextInstruction, AttrInstruction), node): return # Otherwise, perform a normal operation. @@ -704,6 +761,7 @@ else: self.dispatch(arg) self.new_op(StoreFrame(frame_pos)) + employed_positions.add(frame_pos) # Check to see if the first argument is appropriate (compatible with @@ -857,7 +915,7 @@ # Get the method on temp. - self._generateAttr(node, method, self.attr_load_instructions) + self._generateAttr(node, method, self.attribute_load_instructions) # Add exception handling to the method acquisition instructions where # the attribute access cannot be resolved at compile-time. @@ -934,7 +992,7 @@ # Get left method on temp1. - self._generateAttr(node, left_method, self.attr_load_instructions) + self._generateAttr(node, left_method, self.attribute_load_instructions) # Add exception handling to the method acquisition instructions where # the attribute access cannot be resolved at compile-time. @@ -974,7 +1032,7 @@ # Get right method on temp2. - self._generateAttr(node, right_method, self.attr_load_instructions) + self._generateAttr(node, right_method, self.attribute_load_instructions) # Add exception handling to the method acquisition instructions where # the attribute access cannot be resolved at compile-time. @@ -1043,25 +1101,22 @@ def visitAssign(self, node): self.dispatch(node.expr) - self.expr_temp = self._optimise_temp_storage() + self.record_value() for n in node.nodes: self.dispatch(n) - self.discard_temp(self.expr_temp) - self.expr_temp = None + self.discard_value() def visitAssAttr(self, node): self._visitAttr(node, self.attribute_store_instructions) - if self.active is not None: - self.active.source = self.expr_temp + self.set_source() def visitAssList(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "AssList") def visitAssName(self, node): self._visitName(node, (StoreName, StoreAddress)) - if self.active is not None: - self.active.source = self.expr_temp + self.set_source() visitAssTuple = visitAssList @@ -1102,7 +1157,10 @@ # Store the name. self.new_op(LoadConst(node.unit)) + self.record_value() self._visitName(node, (StoreName, StoreAddress)) + self.set_source() + self.discard_value() # Visit the code. @@ -1139,6 +1197,7 @@ def visitDiscard(self, node): self.dispatch(node.expr) + self._optimise_unused_results() def visitDiv(self, node): self._visitBinary(node, "__div__", "__rdiv__") @@ -1161,7 +1220,7 @@ self._startCallFunc() self.dispatch(node.list) - self._generateAttr(node, "__iter__", self.attr_load_instructions) + self._generateAttr(node, "__iter__", self.attribute_load_instructions) temp = self._generateCallFunc([], node) self._doCallFunc(temp) self._endCallFunc(temp) @@ -1176,7 +1235,7 @@ self._startCallFunc() self.new_op(temp_iterator) - self._generateAttr(node, "next", self.attr_load_instructions) + self._generateAttr(node, "next", self.attribute_load_instructions) temp = self._generateCallFunc([], node) self._doCallFunc(temp) self._endCallFunc(temp) @@ -1226,13 +1285,21 @@ if self.unit is not node.unit: self.new_op(LoadConst(node.unit)) + + self.record_value() self._visitName(node, (StoreName, StoreAddress)) + self.set_source() + self.discard_value() # Generate the default initialisation code. for attr, default in zip(node.unit.default_attrs, node.unit.defaults): self.dispatch(default) + + self.record_value() self.new_op(StoreAddress(attr)) + self.set_source() + self.discard_value() # Visiting of the code occurs when get_code is invoked on this node. @@ -1240,6 +1307,8 @@ self.dispatch(node.code) if not isinstance(self.last_op(), Return): self.dispatch(compiler.ast.Name("None")) + self.new_op(StoreResult()) + self.new_op(Return()) def visitGenExpr(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "GenExpr") @@ -1377,6 +1446,8 @@ self.dispatch(node.value) else: self.dispatch(compiler.ast.Name("None")) + + self.new_op(StoreResult()) self.new_op(Return()) def visitRightShift(self, node): diff -r bc2c5bb980a8 -r 3bf7d0e10c17 micropython/data.py --- a/micropython/data.py Sat Jul 19 00:39:50 2008 +0200 +++ b/micropython/data.py Sat Jul 19 21:26:47 2008 +0200 @@ -86,11 +86,7 @@ self.global_namespace.set(name, value, 0) else: attr = self._set(name, value) - - # NOTE: Insist on assignments with known values. - - if value is not None: - attr.update(value, single_assignment) + attr.update(value, single_assignment) def set_module(self, name, value): @@ -222,7 +218,9 @@ self.assignments += 1 else: self.assignments += AtLeast(1) - self.assignment_values.add(value) + + if value is not None: + self.assignment_values.add(value) def via_instance(self): diff -r bc2c5bb980a8 -r 3bf7d0e10c17 micropython/rsvp.py --- a/micropython/rsvp.py Sat Jul 19 00:39:50 2008 +0200 +++ b/micropython/rsvp.py Sat Jul 19 21:26:47 2008 +0200 @@ -61,6 +61,8 @@ return " <- (%r, %r)" % (self.input, self.source) else: return " <- %r" % self.input + elif self.source is not None: + return " <-- %r" % self.source else: return "" @@ -160,7 +162,6 @@ class LoadAddress(Address): "Load the object from the given fixed attribute address." class StoreAddress(Address): "Store an object in the given fixed attribute address." class LoadAddressContext(Address): "Load the object from the given fixed attribute address, changing the context." -class StoreAddressContext(Address): "Store an object in the given fixed attribute address, changing the context." class MakeObject(Instruction): "Make a new object. There isn't a complementary DropObject." # Access to address-relative data. @@ -184,8 +185,9 @@ # Invocation-related instructions, using a special result "register". class JumpWithFrame(Instruction): "Jump, adopting the invocation frame, to the callable found as the current value." -class Return(Instruction): "Return a value from a subprogram." +class Return(Instruction): "Return from a subprogram." class LoadResult(Instruction): "Load a returned value." +class StoreResult(Instruction): "Store a value to be returned." # Branch-related instructions. diff -r bc2c5bb980a8 -r 3bf7d0e10c17 tests/attributes.py --- a/tests/attributes.py Sat Jul 19 00:39:50 2008 +0200 +++ b/tests/attributes.py Sat Jul 19 21:26:47 2008 +0200 @@ -9,7 +9,7 @@ def update(self, value): self.attr = value - self.clsattr = value + C.clsattr = value C C.clsattr diff -r bc2c5bb980a8 -r 3bf7d0e10c17 tests/failure/attributes.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/failure/attributes.py Sat Jul 19 21:26:47 2008 +0200 @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +class C: + clsattr = 123 + + def __init__(self, value): + self.instattr = value + self.clsattr + + def update(self, value): + self.attr = value + self.clsattr = value + C.clsattr = value + +C +C.clsattr + +# vim: tabstop=4 expandtab shiftwidth=4