# HG changeset patch # User Paul Boddie # Date 1219452115 -7200 # Node ID 146c1f0da0d1e0e731910bf7acbff1023d2066d8 # Parent 93404fc56e5df4bc725f841a8294b954d0bca4ae Removed various RSVP instructions, introducing generated code for LoadBoolean and TestBoolean. Improved various RSVP instruction implementations. Removed _propagateAttributeError from Translation. Made _generateAttr use the active value instruction, not the active instruction, when optimising. diff -r 93404fc56e5d -r 146c1f0da0d1 micropython/ast.py --- a/micropython/ast.py Fri Aug 22 00:50:59 2008 +0200 +++ b/micropython/ast.py Sat Aug 23 02:41:55 2008 +0200 @@ -48,7 +48,7 @@ # Instructions which affect the current value. current_value_instructions = (LoadConst, LoadName, LoadTemp, LoadAddress, LoadAddressContext, LoadAttr, LoadAttrIndex, - LoadCallable, LoadContext, LoadResult, LoadException, LoadBoolean, MakeObject) + LoadCallable, LoadContext, LoadResult, LoadException, MakeObject) def __init__(self, module, importer, optimisations=None): @@ -295,6 +295,15 @@ self.remove_op() self.new_op(op) + def replace_active_value(self, op): + + """ + Replace the value-providing active instruction with 'op' if appropriate. + """ + + self.remove_active_value() + self.new_op(op) + def last_op(self): "Return the last added instruction." @@ -345,7 +354,10 @@ def _is_simple_input(self, instruction): - "Return whether 'instruction' provides a simple input." + """ + Return whether 'instruction' provides a simple input (typically a load + instruction). + """ return isinstance(instruction, (LoadConst, LoadName, LoadTemp, LoadResult, LoadAddress, MakeObject)) @@ -357,7 +369,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, CheckSelf, # as one of the operands + TestIdentity, TestIdentityAddress, CheckSelf, # as one of the operands LoadContext, # as the object providing the result JumpWithFrame # as the target )) @@ -635,8 +647,8 @@ # Where the last operation (defining the attribute owner) yields a # constant... - if self._is_constant_input(self.active): - last = self.active + if self._have_constant_input(): + last = self.active_value # Get the details of the access. @@ -673,7 +685,7 @@ # Produce a suitable instruction. if AddressInstruction is not None: - self.replace_op(AddressInstruction(pos)) + self.replace_active_value(AddressInstruction(pos)) else: raise TranslateError(self.module.full_name(), node, "Storing of class or module attribute %r via an object is not permitted." % attrname) @@ -941,8 +953,7 @@ # Set the frame size. - self.frame_makers[-1].attr = nargs_max - self.frame_makers.pop() + self._endCallFuncArgs(nargs_max) # Or generate instructions to do this at run-time. # NOTE: CheckFrame has to check the number of arguments and to fill in @@ -959,8 +970,7 @@ # Set the frame size. - self.frame_makers[-1].attr = max_pos + 1 - self.frame_makers.pop() + self._endCallFuncArgs(max_pos + 1) def _generateCallFuncDefaultArgs(self, target, temp, nargs_min, nargs_max, employed_positions): @@ -995,6 +1005,13 @@ self.new_op(instruction) self.new_op(JumpWithFrame()) + def _endCallFuncArgs(self, nargs): + + "Set the frame size." + + self.frame_makers[-1].attr = nargs + self.frame_makers.pop() + def _endCallFunc(self, instruction=None, load_result=1): "Finish the invocation and tidy up afterwards." @@ -1123,6 +1140,7 @@ self.new_op(temp) # Explicit context as first argument. self.new_op(StoreFrame(0)) + self._endCallFuncArgs(1) self._doCallFunc(temp_method) self._endCallFunc(temp_method) self.new_op(Jump(end_label)) @@ -1238,6 +1256,7 @@ self.new_op(StoreFrame(0)) self.new_op(temp2) self.new_op(StoreFrame(1)) + self._endCallFuncArgs(2) self._doCallFunc(temp_method) self._endCallFunc(temp_method) @@ -1265,23 +1284,6 @@ self.new_op(CheckException()) self.new_op(JumpIfTrue(end_call_label)) - def _propagateAttributeError(self, node): - - """ - Add exception raising to the method acquisition instructions where the - attribute access cannot be resolved at compile-time. - """ - - if not self._optimise_known_target(): - continue_label = self.new_label() - - self.load_builtin("AttributeError", node) - self.new_op(CheckException()) - self.new_op(JumpIfFalse(continue_label)) - self.new_op(RaiseException()) - - self.set_label(continue_label) - def _generateSequence(self, sequence_type, node): "Make a sequence of 'sequence_type' for the given program 'node'." @@ -1300,6 +1302,51 @@ self.new_op(temp) self.discard_temp(temp) + def _generateTestBoolean(self, node): + + """ + Generate a test of the boolean status of the current value for the given + program 'node'. + """ + + temp = self._optimise_temp_storage() + + # Get method on temp. + # NOTE: Using __bool__ instead of __nonzero__. + + self._generateAttr(node, "__bool__", self.attribute_load_instructions) + temp_method = self._optimise_temp_storage() + + self._startCallFunc() + self.new_op(temp) + self.new_op(StoreFrame(0)) + self._endCallFuncArgs(1) + self._doCallFunc(temp_method) + self._endCallFunc(temp_method) + + # Convert result to boolean (a StoreBoolean operation). + + self.new_op(TestIdentityAddress(self.get_builtin("True", node))) + + def _generateLoadBoolean(self, node): + + """ + Generate instructions to load the appropriate value given the current + boolean status. + """ + + true_label = self.new_label() + end_label = self.new_label() + + self.new_op(JumpIfTrue(true_label)) + self.load_builtin("False", node) + self.new_op(Jump(end_label)) + + self.set_label(true_label) + self.load_builtin("True", node) + + self.set_label(end_label) + # Concrete visitor methods. def visitAdd(self, node): @@ -1310,7 +1357,7 @@ for n in node.nodes[:-1]: self.dispatch(n) - self.new_op(TestBoolean()) + self._generateTestBoolean(n) self.new_op(JumpIfFalse(next_label)) self.dispatch(node.nodes[-1]) @@ -1425,7 +1472,7 @@ # Generate method call using evaluated argument and next node. self._generateBinary(node, temp1, temp2, left_method, right_method) - self.new_op(TestBoolean()) + self._generateTestBoolean(node) else: # Deal with the special operators. @@ -1447,8 +1494,6 @@ self._generateAttr(node, "__contains__", self.attribute_load_instructions) temp_method = self._optimise_temp_storage() - self._propagateAttributeError(node) - # Add arguments. # NOTE: No support for defaults. @@ -1456,9 +1501,10 @@ self.new_op(StoreFrame(0)) self.new_op(temp1) self.new_op(StoreFrame(1)) + self._endCallFuncArgs(2) self._doCallFunc(temp_method) self._endCallFunc(temp_method) - self.new_op(TestBoolean()) + self._generateTestBoolean(node) if op_name.find("not") != -1: self.new_op(InvertBoolean()) @@ -1477,7 +1523,7 @@ # Yield the appropriate value. - self.new_op(LoadBoolean()) + self._generateLoadBoolean(node) def visitConst(self, node): const = self.module.constant_values[node.value] @@ -1716,19 +1762,9 @@ true_label = self.new_label() self.dispatch(node.expr) - self.new_op(TestBoolean()) + self._generateTestBoolean(node.expr) self.new_op(InvertBoolean()) - self.new_op(LoadBoolean()) - - # The equivalent of InvertBoolean/LoadBoolean. - - #self.new_op(JumpIfTrue(true_label)) - #self.load_builtin("True", node) - #self.new_op(Jump(next_label)) - - #self.set_label(true_label) - #self.load_builtin("False", node) - #self.set_label(next_label) + self._generateLoadBoolean(node) # Prevent incorrect optimisation. @@ -1740,7 +1776,7 @@ for n in node.nodes[:-1]: self.dispatch(n) - self.new_op(TestBoolean()) + self._generateTestBoolean(n) self.new_op(JumpIfTrue(next_label)) self.dispatch(node.nodes[-1]) @@ -1773,6 +1809,7 @@ self._startCallFunc() self.new_op(temp_arg) self.new_op(StoreFrame(0)) + self._endCallFuncArgs(1) self._doCallFunc(temp) self._endCallFunc(temp) diff -r 93404fc56e5d -r 146c1f0da0d1 micropython/rsvp.py --- a/micropython/rsvp.py Fri Aug 22 00:50:59 2008 +0200 +++ b/micropython/rsvp.py Sat Aug 23 02:41:55 2008 +0200 @@ -207,10 +207,8 @@ # Test instructions, operating on the boolean status register. -class TestBoolean(Instruction): "Test whether the current value is a true value, setting the boolean status." class TestIdentity(Instruction): "Test whether the current value is identical to the source value, setting the boolean status." class TestIdentityAddress(Address): "Test whether the current value is identical to the given address, setting the boolean status." class InvertBoolean(Instruction): "Invert the boolean status." -class LoadBoolean(Instruction): "Load the appropriate value into the current value for the boolean status." # vim: tabstop=4 expandtab shiftwidth=4 diff -r 93404fc56e5d -r 146c1f0da0d1 rsvp.py --- a/rsvp.py Fri Aug 22 00:50:59 2008 +0200 +++ b/rsvp.py Sat Aug 23 02:41:55 2008 +0200 @@ -246,6 +246,7 @@ def StoreAttr(self): context, ref = self.value + # Target should already be an instance. self.save(ref + self.operand, self.source) def LoadAttrIndex(self): @@ -264,9 +265,23 @@ self.value = self.load(ref + offset) else: # NOTE: This should cause an attribute error. - self.value = None + raise Exception, "LoadAttrIndex % r" % element - def StoreAttrIndex(self): pass + def StoreAttrIndex(self): + context, ref = self.value + code = self.load(ref) # + 0 (the classcode) + element = self.objtable[code + self.operand] + found_code, class_attr, replace_context, offset = element + if found_code == code: + if class_attr: + # NOTE: This should cause an attribute or type error. + # Class attributes cannot be changed at run-time. + raise Exception, "StoreAttrIndex % r" % element + else: + self.save(ref + offset, self.source) + else: + # NOTE: This should cause an attribute error. + raise Exception, "StoreAttrIndex % r" % element # NOTE: LoadAttrIndexContext is a possibility if a particular attribute can always be overridden. @@ -285,6 +300,21 @@ frame = self.invocation_sp_stack.pop() self.frame_stack = self.frame_stack[:frame] # reset stack before call + def StoreFrame(self): + frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame + self.frame_stack[frame + self.operand] = self.value + + def StoreFrameIndex(self): + frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame + code = self.load(ref) # + 0 (the functioncode) + element = self.objtable[code + self.operand] + found_code, offset = element + if found_code == code: + self.frame_stack[frame + offset] = self.value + else: + # NOTE: This should cause an argument error. + raise Exception, "StoreFrameIndex % r" % element + def CheckFrame(self): pass def CheckSelf(self): pass @@ -304,15 +334,6 @@ def StoreResult(self): self.result = self.value - def LoadException(self): - self.value = self.exception - - def StoreException(self): - self.exception = self.value - - def LoadBoolean(self): - self.value = self.status - def Jump(self): return self.operand @@ -324,8 +345,31 @@ if not self.status: return self.operand - def StoreFrame(self): - frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame - self.frame_stack[frame + self.operand] = self.value + def LoadException(self): + self.value = self.exception + + def StoreException(self): + self.exception = self.value + + def RaiseException(self): + return self.handler_stack.pop() + + def PushHandler(self): + self.handler_stack.append(self.operand) + + def PopHandler(self): + self.handler_stack.pop() + + def CheckException(self): + self.status = self.value[1] == self.exception + + def TestIdentity(self): + self.status = self.value[1] == self.source + + def TestIdentityAddress(self): + self.status = self.value[1] == self.operand + + # LoadBoolean is implemented in the generated code. + # StoreBoolean is implemented by testing against the True value. # vim: tabstop=4 expandtab shiftwidth=4