# HG changeset patch # User Paul Boddie # Date 1315679632 -7200 # Node ID 2c50d15ed110b2d38371612811b9fe42c4261d82 # Parent 835c936bfd461b66dd3be47ee17352c336686e9b Fixed optimised attribute accesses involving constant instances. Introduced recording of target-related instructions so that they can all be removed when constant assignments are detected. diff -r 835c936bfd46 -r 2c50d15ed110 micropython/ast.py --- a/micropython/ast.py Sat Sep 10 20:29:30 2011 +0200 +++ b/micropython/ast.py Sat Sep 10 20:33:52 2011 +0200 @@ -211,9 +211,6 @@ self.set_block(end_block) - # Make a separate instruction to prevent previous temp accesses from - # being altered by assign_value. - temp = LoadTemp(temp_pos) self.new_op(temp) self.discard_temp(temp) @@ -245,9 +242,6 @@ self.set_block(end_block) - # Make a separate instruction to prevent previous temp accesses from - # being altered by assign_value. - temp = LoadTemp(temp_pos) self.new_op(temp) self.discard_temp(temp) @@ -301,6 +295,7 @@ if op_name.startswith("is"): self.new_op(temp1) self.record_value() + self.start_target() self.new_op(temp2) self.new_op(TestIdentity(target="status")) self.assign_value() @@ -454,6 +449,7 @@ "Assign the assignment expression to the recipient 'node'." + self.start_target() self._visitAttr(node, self.optimiser.get_attribute_store_instructions()) self.assign_value() @@ -493,6 +489,7 @@ if hasattr(node, "flags") and node.flags == "OP_DELETE": raise TranslationNotImplementedError("AssName(OP_DELETE)") + self.start_target() self._visitName(node, self.name_store_instructions) self.assign_value() @@ -531,6 +528,7 @@ self.new_op(LoadClass(node.unit)) self.record_value() + self.start_target() self._visitName(node, self.name_store_instructions) self.assign_value() self.discard_value() @@ -559,6 +557,7 @@ self.record_value() + self.start_target() self._visitName(node, self.name_store_instructions) # AssName equivalent self.assign_value() self.discard_value() diff -r 835c936bfd46 -r 2c50d15ed110 micropython/code.py --- a/micropython/code.py Sat Sep 10 20:29:30 2011 +0200 +++ b/micropython/code.py Sat Sep 10 20:33:52 2011 +0200 @@ -45,6 +45,10 @@ self.expr_temp = [] + # The start of the current assignment target instructions. + + self.target_start = None + # Wiring within the code. self.labels = {} @@ -144,50 +148,78 @@ self.discard_temp(self.expr_temp.pop()) + def start_target(self): + + """ + Start recording instructions used to define the target of an assignment. + """ + + self.target_start = len(self.blocks[-1]) + + def remove_target_ops(self): + + "Remove the assignment target instructions." + + del self.blocks[-1].code[self.target_start:] + self.target_start = None + self.optimiser.clear_active() + + def get_target_ops(self): + + "Return the assignment target instructions." + + return self.blocks[-1].code[self.target_start:] + def assign_value(self, expr=None): """ Set the source of an assignment using 'expr' or the current assignment - value. This sets the source register of the current instruction. + value. This may set the source register of the current instruction. """ - if expr is None: - expr = self.expr_temp[-1] + expr = expr or self.expr_temp[-1] + + # Optimise away constant storage if appropriate. - # Optimise away constant storage if appropriate. - - if self.optimiser.optimise_constant_storage(expr): - self.remove_op() - return + if self.optimiser.optimise_constant_storage(expr): + self.remove_target_ops() + return # Otherwise, insert the assignment source. - if expr is not None: - expr_copy = expr.copy() - assign_op = self.last_op() + expr_copy = expr.copy() + assign_ops = self.get_target_ops() + + if not assign_ops: + self.target_start = None + return - # Either insert the instruction yielding the value and adjust the - # assignment source. + assign_op = assign_ops[-1] - expr_copy.target = "source" + # Either insert the instruction yielding the value and adjust the + # assignment source. - if self.insert_op(-1, expr_copy): - assign_op.source = "source" - self.update_temp(expr, expr_copy) + expr_copy.target = "source" - # (Now, the instruction need not be inserted.) + if self.insert_op(-1, expr_copy): + assign_op.source = "source" + self.update_temp(expr, expr_copy) - # Or transfer the working value to the source register. + # (Now, the instruction need not be inserted.) + + # Or transfer the working value to the source register. - elif assign_op.working == "working": - self.insert_op(-1, Transfer(source="working_context", target="source_context")) - self.insert_op(-1, Transfer(source="working", target="source")) - assign_op.source = "source" + elif assign_op.working == "working": + self.insert_op(-1, Transfer(source="working_context", target="source_context")) + self.insert_op(-1, Transfer(source="working", target="source")) + assign_op.source = "source" - # Or let the assignment use the working register. + # Or let the assignment use the working register. - else: - assign_op.source = "working" + else: + assign_op.source = "working" + + self.target_start = None def set_target(self, target): @@ -285,7 +317,11 @@ if isinstance(instruction, LoadTemp) and instruction.attr is not None: temp_position = instruction.attr - self.unit.all_local_usage self.free_temp(temp_position) - self.optimiser.ignore_active_value() + + # Prevent any requested active value from generating instructions now + # that our interest in it has passed. + + self.optimiser.ignore_active_value() def free_temp(self, temp_position): @@ -361,6 +397,7 @@ op = self.blocks[-1].code.pop() self.optimiser.clear_active() + return op def replace_op(self, op): @@ -375,8 +412,9 @@ Replace the value-providing active instruction with 'op' if appropriate. """ - self.optimiser.remove_active_value() + removed = self.optimiser.remove_active_value() self.new_op(op) + return removed def last_op(self): diff -r 835c936bfd46 -r 2c50d15ed110 micropython/program.py --- a/micropython/program.py Sat Sep 10 20:29:30 2011 +0200 +++ b/micropython/program.py Sat Sep 10 20:33:52 2011 +0200 @@ -43,6 +43,9 @@ def append(self, op): self.code.append(op) + def __len__(self): + return len(self.code) + class DataValue: "A representation of a raw program value." diff -r 835c936bfd46 -r 2c50d15ed110 micropython/trans.py --- a/micropython/trans.py Sat Sep 10 20:29:30 2011 +0200 +++ b/micropython/trans.py Sat Sep 10 20:33:52 2011 +0200 @@ -190,11 +190,8 @@ target_plus_name = self.optimiser.optimise_constant_accessor() # Only try and discover the position if the target can be resolved. - # Since instances cannot be constants, this involves classes and - # modules. - # It is acceptable to replace the instruction providing the constant - # input because doing so does not lose any input information required by - # the replacement instructions. + # Since instances cannot be constants in general, this involves classes + # and modules, but constants known at compile-time must also be handled. if target_plus_name is not None: target, target_name = target_plus_name @@ -219,7 +216,20 @@ # Produce a suitable instruction. if AddressInstruction is not None: - self.replace_active_value(AddressInstruction(attr)) + + # Where the target is a constant instance, the constant input + # needs to be retained as the context of the resulting + # attribute. + + if isinstance(target, Instance): + self.new_op(AddressContextInstruction(attr)) + + # It is acceptable to replace the instruction providing the + # constant input because doing so does not lose any input + # information required by the replacement instructions. + + else: + self.replace_active_value(AddressInstruction(attr)) else: raise TranslateError("Storing of class or module attribute %r via an object is not permitted." % attrname) @@ -631,6 +641,7 @@ self.dispatch(arg.expr) self.record_value() + self.start_target() # Store the source value using the callable's parameter # table information. @@ -979,6 +990,7 @@ else: self.record_value() + self.start_target() self.new_op(StoreName(fn[parameter])) self.assign_value() self.discard_value() @@ -1011,6 +1023,7 @@ self.dispatch(default) self.record_value() + self.start_target() if dynamic: self.new_op(temp) self.new_op(StoreAttr(attr)) @@ -1239,6 +1252,8 @@ self.make_instance(self.get_builtin_class("list"), 1) list_temp = self.get_temp() + + self.start_target() self.new_op(list_temp) self.new_op(StoreAttr(Attr(0, None, None))) # _elements is at position 0 self.assign_value() @@ -1267,6 +1282,7 @@ position 'i' with the given starting 'offset'. """ + self.start_target() self.new_op(temp) self.new_op(StoreAttr(Attr(i + offset, None, None))) self.assign_value()