# HG changeset patch # User Paul Boddie # Date 1284248881 -7200 # Node ID 774448304582fa6067cf7c33135eb9621ae10dd5 # Parent d5ee53c83a45b15f0bcfb74cb5d79032427364a9 Propagated more detailed usage information to the importer in order to deduce types for defining users, setting the _attrtypes annotation on user nodes. Introduced the _attrdefs annotation which is used when looking up defining users and thus accessor types for attribute access nodes. Fixed a revised example of attribute usage optimisations. Added RSVP return address markings in the memory display produced by the show_memory method. diff -r d5ee53c83a45 -r 774448304582 micropython/__init__.py --- a/micropython/__init__.py Sat Sep 11 21:45:24 2010 +0200 +++ b/micropython/__init__.py Sun Sep 12 01:48:01 2010 +0200 @@ -448,18 +448,28 @@ if not self.name_references.has_key(from_name): self.name_references[from_name] = set() - self.name_references[from_name].add((name,)) - def use_names(self, names, from_name): + attrnames = (name,) + self.name_references[from_name].add((None, None, attrnames)) + + def use_names(self, user, name, usage, from_name): """ - Register the given 'names' as being used in the program from within an - object with the specified 'from_name'. + For the given attribute 'user' (which may be None if no specific user is + given), register for the given 'name' the given attribute 'usage' + (combinations of attribute names), noting the scope of this usage as + being the program object with the specified 'from_name'. """ if not self.name_references.has_key(from_name): self.name_references[from_name] = set() - self.name_references[from_name].add(names) + + all_attrnames = set() + + for attrnames in usage: + all_attrnames.update(attrnames) + + self.name_references[from_name].add((user, name, tuple(all_attrnames))) def use_specific_name(self, objname, attrname, from_name): @@ -546,30 +556,42 @@ # Get name references and find possible objects which support such # combinations of attribute names. - for names in self.name_references.get(from_name, []): - objnames = objtable.all_possible_objects(names) - if not objnames: - print "Warning: usage in %r finds no object supporting all attributes %r" % (from_name, names) - objnames = objtable.any_possible_objects(names) - if not objnames: - print "Warning: usage in %r finds no object supporting any attributes %r" % (from_name, names) + for user, name, attrnames in self.name_references.get(from_name, []): + + # Using all attribute names for a particular name, attempt to get + # specific object types. + + objtypes = objtable.all_possible_objects_plus_status(attrnames) + if not objtypes: + print "Warning: usage in %r finds no object supporting all attributes %r" % (from_name, attrnames) + objtypes = objtable.any_possible_objects_plus_status(attrnames) + if not objtypes: + print "Warning: usage in %r finds no object supporting any attributes %r" % (from_name, attrnames) + + # Record the object types for generating guards. + + if user is not None: + if not hasattr(user, "_attrtypes"): + user._attrtypes = {} + + user._attrtypes[name] = objtypes # For each suggested object, consider each attribute given by the # names. - for objname in objnames: - for name in names: + for objname, is_static in objtypes: + for attrname in attrnames: # Visit attributes of objects known to be used. if objname in self.attributes_used: - self.use_attribute(objname, name) - self._collect_attributes(objname + "." + name, objtable) + self.use_attribute(objname, attrname) + self._collect_attributes(objname + "." + attrname, objtable) # Record attributes of other objects for potential visiting. else: - self.add_attribute_to_visit(objname, name) + self.add_attribute_to_visit(objname, attrname) # Get specific name references and visit the referenced objects. diff -r d5ee53c83a45 -r 774448304582 micropython/ast.py --- a/micropython/ast.py Sat Sep 11 21:45:24 2010 +0200 +++ b/micropython/ast.py Sun Sep 12 01:48:01 2010 +0200 @@ -524,8 +524,7 @@ # Add any attribute usage guards. - if self.optimiser.should_optimise_accesses_by_attribute_usage() and hasattr(node, "_attrnames"): - self._generateGuards(node) + self._generateGuards(node) visitAssTuple = visitAssList diff -r d5ee53c83a45 -r 774448304582 micropython/common.py --- a/micropython/common.py Sat Sep 11 21:45:24 2010 +0200 +++ b/micropython/common.py Sun Sep 12 01:48:01 2010 +0200 @@ -61,8 +61,16 @@ if hasattr(node, "_attrusers"): target_names = set() + # Visit each attribute user. + for user in node._attrusers: - target_names.update(self.objtable.all_possible_objects(user._attrnames[node._username])) + + # Since users such as branches may not provide type information, + # attempt to find defining users. + + for def_user in user._attrdefs or [user]: + for target_name, is_static in def_user._attrtypes[node._username]: + target_names.add(target_name) return target_names diff -r d5ee53c83a45 -r 774448304582 micropython/data.py --- a/micropython/data.py Sat Sep 11 21:45:24 2010 +0200 +++ b/micropython/data.py Sun Sep 12 01:48:01 2010 +0200 @@ -364,40 +364,39 @@ importer = module and module.importer if importer is not None: - for names in self.get_all_attribute_usage(): - importer.use_names(names, self.full_name()) - - def get_all_attribute_usage(self): - - """ - Return a set of all usage tuples for attributes used with the different - local names. - """ - - usage = set() - for user in self.all_attribute_users: - - # First, visit the contributors and combine their usage with the - # usage for each user. - - user._attrcombined = combined_usage = self.get_usage_from_contributors(user) - - for name, all_usage in combined_usage.items(): - for attrnames in all_usage: - usage.add(tuple(attrnames)) - - return usage + + # Visit each user and examine the attribute usage for each name. + + for user in self.all_attribute_users: + + # First, visit the contributors and combine their attribute + # usage with the usage recorded directly on the user. + + contributors, combined_usage = self.get_usage_from_contributors(user) + user._attrcombined = combined_usage + + # Record the defining user on each contributor. + + for contributor in contributors: + contributor._attrdefs.append(user) + + # Then, tell the importer about the usage. + + for name, usage in combined_usage.items(): + importer.use_names(user, name, usage, self.full_name()) def get_usage_from_contributors(self, node): """ Obtain usage information from the given 'node', combined with usage - details from its contributors, returning a dictionary mapping names to - lists of usage possibilities. + details from its contributors, returning a tuple containing a set of all + contributors employed along with a dictionary mapping names to lists of + usage possibilities (each a collection of attribute names). """ usage = {} contributor_usage = {} + all_contributors = set() # Visit each contributor, gathering usage for each name. @@ -407,11 +406,18 @@ # This gathers usage for each name such as {(a, b), (c, d)} and # {(a, b), (e, f)} into a single set {(a, b), (c, d), (e, f)}. - for name, all_usage in self.get_usage_from_contributors(contributor).items(): + contributors, contributed_usage = self.get_usage_from_contributors(contributor) + + for name, all_usage in contributed_usage.items(): if not contributor_usage.has_key(name): contributor_usage[name] = set() contributor_usage[name].update(all_usage) + # Record all contributors. + + all_contributors.add(contributor) + all_contributors.update(contributors) + # Then get the resulting usage. contributed_names = contributor_usage.keys() @@ -443,7 +449,7 @@ if name not in contributed_names: usage[name] = set([tuple(current_usage[name])]) - return usage + return all_contributors, usage def use_attribute(self, name, attrname): @@ -532,12 +538,22 @@ node._attrnames[name] = set() def _init_attribute_user(self, node): + + # Attribute usage for names. + if not hasattr(node, "_attrnames"): node._attrnames = {} + # Branches contributing usage to this node. + if not hasattr(node, "_attrbranches"): node._attrbranches = [] + # Definitions receiving usage from this node. + + if not hasattr(node, "_attrdefs"): + node._attrdefs = [] + # Branch management methods. def _new_branchpoint(self): diff -r d5ee53c83a45 -r 774448304582 micropython/inspect.py --- a/micropython/inspect.py Sat Sep 11 21:45:24 2010 +0200 +++ b/micropython/inspect.py Sun Sep 12 01:48:01 2010 +0200 @@ -451,7 +451,13 @@ # Note usage of the attribute where a local is involved. if expr.parent is self.get_namespace(): - node._attrusers = self.use_attribute(expr.name, attrname) + + # NOTE: Revisiting of nodes may occur for loops. + + if not hasattr(node, "_attrusers"): + node._attrusers = set() + + node._attrusers.update(self.use_attribute(expr.name, attrname)) node._username = expr.name else: self.use_name(attrname, node.expr) @@ -575,7 +581,13 @@ # Note usage of the attribute where a local is involved. if expr.parent is self.get_namespace(): - node._attrusers = self.use_attribute(expr.name, node.attrname) + + # NOTE: Revisiting of nodes may occur for loops. + + if not hasattr(node, "_attrusers"): + node._attrusers = set() + + node._attrusers.update(self.use_attribute(expr.name, node.attrname)) node._username = expr.name return None @@ -1032,10 +1044,19 @@ in_loop = self.in_loop self.in_loop = 1 + + # The test is evaluated initially. + self.dispatch(node.test) + self.new_branch(node) self.dispatch(node.body) + + # The test is evaluated again within the loop. + + self.dispatch(node.test) self.shelve_branch() + self.in_loop = in_loop # Maintain a branch for the else clause. diff -r d5ee53c83a45 -r 774448304582 micropython/trans.py --- a/micropython/trans.py Sat Sep 11 21:45:24 2010 +0200 +++ b/micropython/trans.py Sun Sep 12 01:48:01 2010 +0200 @@ -310,17 +310,12 @@ def _generateGuards(self, node): - if not (self.optimiser.should_optimise_accesses_by_attribute_usage() and hasattr(node, "_attrnames")): + if not (self.optimiser.should_optimise_accesses_by_attribute_usage() and hasattr(node, "_attrtypes")): return # For each name, attempt to restrict the type employed. - for name, names_used in node._attrnames.items(): - - # Get the names of all object types supporting these names. - - targets = self.objtable.all_possible_objects_plus_status(names_used) - #found_target_names = [target_name for (target_name, is_static) in targets] + for name, targets in node._attrtypes.items(): # NOTE: Need to merge targets using the same type but suggesting # NOTE: different kinds of attributes (instance vs. class). @@ -1089,8 +1084,7 @@ # Add any attribute usage guards. - if self.optimiser.should_optimise_accesses_by_attribute_usage() and hasattr(node, "_attrnames"): - self._generateGuards(node) + self._generateGuards(node) # Visit the actual code. diff -r d5ee53c83a45 -r 774448304582 rsvp.py --- a/rsvp.py Sat Sep 11 21:45:24 2010 +0200 +++ b/rsvp.py Sun Sep 12 01:48:01 2010 +0200 @@ -182,6 +182,8 @@ location = start + i if location == self.pc: print "->", + elif location in self.pc_stack: + print "..", else: print " ", print "%5d %r" % (location, x) diff -r d5ee53c83a45 -r 774448304582 tests/attribute_access_type_restriction_loop_accumulation.py --- a/tests/attribute_access_type_restriction_loop_accumulation.py Sat Sep 11 21:45:24 2010 +0200 +++ b/tests/attribute_access_type_restriction_loop_accumulation.py Sun Sep 12 01:48:01 2010 +0200 @@ -19,6 +19,9 @@ def g(self): return 3 + def h(self): + return 4 + class E: def e(self): return 4