# HG changeset patch # User Paul Boddie # Date 1209940673 -7200 # Node ID 1f879e94c49f768a9f680ae61cbbf10fdd1160b8 # Parent a550b84e0ea5d61d045b6af7ec3241785a7091e4 Introduced parameters to certain methods which permit the retrieval and/or inspection of previous instructions in alternative sequences; this helps in the production of substitutable instruction sequences related to temporary storage access. Added methods which capture generated code for use with such temporary storage instruction sequences, changing the existing methods to employ sequences instead of single instructions. diff -r a550b84e0ea5 -r 1f879e94c49f micropython/ast.py --- a/micropython/ast.py Sun May 04 20:11:46 2008 +0200 +++ b/micropython/ast.py Mon May 05 00:37:53 2008 +0200 @@ -89,6 +89,7 @@ self.code = None self.temp_position = 0 + self.capture_start = None def calculate_stack_usage(self): max_stack_usage = 0 @@ -195,6 +196,12 @@ self.code.append(op) + def new_ops(self, ops): + + "Add 'ops' to the generated code." + + self.code += ops + def replace_op(self, op): "Replace the last added instruction with 'op'." @@ -207,23 +214,31 @@ del self.code[-n:] - def last_ops(self, n): + def last_ops(self, n, ops=None): "Return the last 'n' added instructions in reverse chronological order." - ops = self.code[-n:] + ops = (ops or self.code)[-n:] ops.reverse() return ops - def last_op(self): + def last_op(self, ops=None): "Return the last added instruction." try: - return self.code[-1] + return (ops or self.code)[-1] except IndexError: return None + def capture_ops(self): + self.capture_start = len(self.code) + + def get_captured_ops(self): + l = self.code[self.capture_start:] + del self.code[self.capture_start:] + return l + # Internal helper methods. def _visitAttr(self, node, classes): @@ -484,13 +499,13 @@ "Make the invocation and tidy up afterwards." - self.new_op(temp) + self.new_ops(temp) self.new_op(JumpWithFrame()) # NOTE: Exception handling required. self.new_op(DropFrame()) - self.discard_temp([temp]) + self.discard_temp(temp) def _visitName(self, node, classes): @@ -563,13 +578,13 @@ def _should_optimise_temp_storage(self): return "temp_storage" in self.optimisations - def _have_constant_input(self, n): - last = self.last_ops(n+1) + def _have_constant_input(self, n, ops=None): + last = self.last_ops(n+1, ops) return len(last) > n and (isinstance(last[n], LoadAddress) and last[n].attr.assignments == 1 or isinstance(last[n], LoadConst)) # and not isinstance(last[n].attr, micropython.inspect.Instance) - def _have_known_target(self): - return self._have_constant_input(0) + def _have_known_target(self, ops=None): + return self._have_constant_input(0, ops) def _have_self_input(self): last = self.last_op() @@ -595,6 +610,8 @@ If no optimisation can be achieved, a StoreTemp instruction is produced and the appropriate LoadTemp instruction is returned. + + All returned instructions are provided in a list. """ if self._should_optimise_temp_storage() and \ @@ -602,11 +619,11 @@ last = self.last_op() self.remove_ops(1) - return last + return [last] else: temp_position = self.reserve_temp(1) self.new_op(StoreTemp(temp_position)) - return LoadTemp(temp_position) + return [LoadTemp(temp_position)] def _optimise_constant_storage(self, instruction, n): @@ -643,7 +660,7 @@ else: return 0 - def _optimise_known_target(self): + def _optimise_known_target(self, ops=None): """ Where the target of an invocation is known, provide information about it @@ -651,8 +668,8 @@ appropriate, get information about the specific initialiser. """ - if self._should_optimise_known_target() and self._have_known_target(): - last = self.last_op() + if self._should_optimise_known_target() and self._have_known_target(ops): + last = self.last_op(ops) target = last.attr.value context = last.attr.parent @@ -730,22 +747,27 @@ # Left method. self._startCallFunc() - self.new_op(temp1) + self.new_ops(temp1) # Get left method on temp1. self._generateAttr(node, left_method, (LoadAddress, LoadAttr, LoadAttrIndex)) temp_method = self._optimise_temp_storage() - if not self._optimise_known_target(): + # Add exception handling to the method acquisition instructions where + # the attribute access cannot be resolved at compile-time. + + if not self._optimise_known_target(temp_method): + self.capture_ops() self.dispatch(compiler.ast.Name("AttributeError")) - self.new_op(CheckException()) - self.new_op(JumpIfTrue(end_left_label)) + temp_method += self.get_captured_ops() + temp_method.append(CheckException()) + temp_method.append(JumpIfTrue(end_left_label)) # Add arguments. - self.new_op(temp1) # Explicit context as first argument. - self.new_op(temp2) + self.new_ops(temp1) # Explicit context as first argument. + self.new_ops(temp2) self._endCallFunc(temp_method) # Test for NotImplemented. @@ -766,22 +788,27 @@ self.set_label(right_label) self._startCallFunc() - self.new_op(temp2) + self.new_ops(temp2) # Get right method on temp2. self._generateAttr(node, right_method, (LoadAddress, LoadAttr, LoadAttrIndex)) temp_method = self._optimise_temp_storage() - if not self._optimise_known_target(): + # Add exception handling to the method acquisition instructions where + # the attribute access cannot be resolved at compile-time. + + if not self._optimise_known_target(temp_method): + self.capture_ops() self.dispatch(compiler.ast.Name("AttributeError")) - self.new_op(CheckException()) - self.new_op(JumpIfTrue(type_error_label)) + temp_method += self.get_captured_ops() + temp_method.append(CheckException()) + temp_method.append(JumpIfTrue(type_error_label)) # Add arguments. - self.new_op(temp2) # Explicit context as first argument. - self.new_op(temp1) + self.new_ops(temp2) # Explicit context as first argument. + self.new_ops(temp1) self._endCallFunc(temp_method) # Test for NotImplemented. @@ -802,9 +829,9 @@ self.set_label(end_label) # Compilation duties... - # NOTE: Potentially remove this when optimised away. - self.discard_temp([temp1, temp2]) + self.discard_temp(temp1) + self.discard_temp(temp2) def visitAdd(self, node): self._visitBinary(node, "__add__", "__radd__")