Lichen

Changeset

854:47b5a25fe02f
2018-07-12 Paul Boddie raw files shortlog changelog graph Merged changes from the default branch. tuple-optimisations
     1.1 --- a/deducer.py	Tue Jul 10 13:28:53 2018 +0200
     1.2 +++ b/deducer.py	Thu Jul 12 18:19:02 2018 +0200
     1.3 @@ -2938,7 +2938,13 @@
     1.4              # Produce an advisory instruction regarding the context.
     1.5  
     1.6              if context_var:
     1.7 -                if context_test in ("ignore", "replace"):
     1.8 +
     1.9 +                # Only verify the context for invocation purposes if a suitable
    1.10 +                # test has been performed.
    1.11 +
    1.12 +                if context_test in ("ignore", "replace") or \
    1.13 +                   final_method in ("access-invoke", "static-invoke"):
    1.14 +
    1.15                      emit(("<context_identity_verified>", context_var))
    1.16                  else:
    1.17                      emit(("<context_identity>", context_var))
     2.1 --- a/generator.py	Tue Jul 10 13:28:53 2018 +0200
     2.2 +++ b/generator.py	Thu Jul 12 18:19:02 2018 +0200
     2.3 @@ -3,7 +3,7 @@
     2.4  """
     2.5  Generate C code from object layouts and other deduced information.
     2.6  
     2.7 -Copyright (C) 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk>
     2.8 +Copyright (C) 2015, 2016, 2017, 2018 Paul Boddie <paul@boddie.org.uk>
     2.9  
    2.10  This program is free software; you can redistribute it and/or modify it under
    2.11  the terms of the GNU General Public License as published by the Free Software
    2.12 @@ -204,14 +204,12 @@
    2.13              # Generate table and structure data.
    2.14  
    2.15              function_instance_attrs = None
    2.16 -            objects = self.optimiser.attr_table.items()
    2.17 +            objects = self.optimiser.all_attrs.items()
    2.18              objects.sort()
    2.19  
    2.20              self.callables = {}
    2.21  
    2.22 -            for ref, indexes in objects:
    2.23 -                attrnames = self.get_attribute_names(indexes)
    2.24 -
    2.25 +            for ref, attrnames in objects:
    2.26                  kind = ref.get_kind()
    2.27                  path = ref.get_origin()
    2.28                  table_name = encode_tablename(kind, path)
    2.29 @@ -613,8 +611,7 @@
    2.30          # Obtain the attributes.
    2.31  
    2.32          cls = ref.get_origin()
    2.33 -        indexes = self.optimiser.attr_table[ref]
    2.34 -        attrnames = self.get_attribute_names(indexes)
    2.35 +        attrnames = self.optimiser.all_attrs[ref]
    2.36          attrs = self.get_instance_attributes(cls, attrnames)
    2.37  
    2.38          # Set the data, if provided.
    2.39 @@ -867,22 +864,6 @@
    2.40          num_parameters = len(parameters) + 1
    2.41          return num_parameters - (defaults and len(defaults) or 0), num_parameters
    2.42  
    2.43 -    def get_attribute_names(self, indexes):
    2.44 -
    2.45 -        """
    2.46 -        Given a list of attribute table 'indexes', return a list of attribute
    2.47 -        names.
    2.48 -        """
    2.49 -
    2.50 -        all_attrnames = self.optimiser.all_attrnames
    2.51 -        attrnames = []
    2.52 -        for i in indexes:
    2.53 -            if i is None:
    2.54 -                attrnames.append(None)
    2.55 -            else:
    2.56 -                attrnames.append(all_attrnames[i])
    2.57 -        return attrnames
    2.58 -
    2.59      def get_static_attributes(self, kind, name, attrnames):
    2.60  
    2.61          """
    2.62 @@ -987,17 +968,8 @@
    2.63          structure of the given 'kind', adding entries to the object 'structure'.
    2.64          """
    2.65  
    2.66 -        # Populate function instance structures for functions.
    2.67 -
    2.68 -        if ref.has_kind("<function>"):
    2.69 -            origin = self.function_type
    2.70 -            structure_ref = Reference("<instance>", self.function_type)
    2.71 -
    2.72 -        # Otherwise, just populate the appropriate structures.
    2.73 -
    2.74 -        else:
    2.75 -            origin = ref.get_origin()
    2.76 -            structure_ref = ref
    2.77 +        structure_ref = self.get_target_structure(ref)
    2.78 +        origin = structure_ref.get_origin()
    2.79  
    2.80          for attrname in self.optimiser.structures[structure_ref]:
    2.81  
    2.82 @@ -1129,6 +1101,20 @@
    2.83  
    2.84                  structure.append(self.encode_member(origin, attrname, attr, kind))
    2.85  
    2.86 +    def get_target_structure(self, ref):
    2.87 +
    2.88 +        "Return the target structure type and reference for 'ref'."
    2.89 +
    2.90 +        # Populate function instance structures for functions.
    2.91 +
    2.92 +        if ref.has_kind("<function>"):
    2.93 +            return Reference("<instance>", self.function_type)
    2.94 +
    2.95 +        # Otherwise, just populate the appropriate structures.
    2.96 +
    2.97 +        else:
    2.98 +            return ref
    2.99 +
   2.100      def encode_member(self, path, name, ref, structure_type):
   2.101  
   2.102          """
     3.1 --- a/lib/__builtins__/int.py	Tue Jul 10 13:28:53 2018 +0200
     3.2 +++ b/lib/__builtins__/int.py	Thu Jul 12 18:19:02 2018 +0200
     3.3 @@ -3,7 +3,7 @@
     3.4  """
     3.5  Integer objects.
     3.6  
     3.7 -Copyright (C) 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk>
     3.8 +Copyright (C) 2015, 2016, 2017, 2018 Paul Boddie <paul@boddie.org.uk>
     3.9  
    3.10  This program is free software; you can redistribute it and/or modify it under
    3.11  the terms of the GNU General Public License as published by the Free Software
    3.12 @@ -19,12 +19,12 @@
    3.13  this program.  If not, see <http://www.gnu.org/licenses/>.
    3.14  """
    3.15  
    3.16 -from __builtins__.operator import _negate
    3.17  from __builtins__.unicode import utf8string
    3.18  from native import get_maxint, get_minint, is_int, \
    3.19 -                   int_add, int_and, int_div, int_eq, int_gt, int_lshift, \
    3.20 -                   int_lt, int_mod, int_mul, int_ne, int_neg, int_not, \
    3.21 -                   int_or, int_pow, int_rshift, int_str, int_sub, int_xor
    3.22 +                   int_add, int_and, int_div, int_eq, int_ge, int_gt, \
    3.23 +                   int_lshift, int_le, int_lt, int_mod, int_mul, int_ne, \
    3.24 +                   int_neg, int_not, int_or, int_pow, int_rshift, int_str, \
    3.25 +                   int_sub, int_xor
    3.26  
    3.27  class int:
    3.28  
    3.29 @@ -140,6 +140,8 @@
    3.30  
    3.31          return self._binary_op_rev(int_div, other)
    3.32  
    3.33 +    # NOTE: To be implemented.
    3.34 +
    3.35      def __floordiv__(self, other): pass
    3.36      def __rfloordiv__(self, other): pass
    3.37      def __ifloordiv__(self, other): pass
    3.38 @@ -189,7 +191,6 @@
    3.39          return self._binary_op_rev(int_rshift, other)
    3.40  
    3.41      __ilshift__ = __lshift__
    3.42 -
    3.43      __irshift__ = __rshift__
    3.44  
    3.45      def __lt__(self, other):
    3.46 @@ -208,13 +209,13 @@
    3.47  
    3.48          "Return whether this int is less than or equal to 'other'."
    3.49  
    3.50 -        return _negate(self.__gt__(other))
    3.51 +        return self._binary_op(int_le, other)
    3.52  
    3.53      def __ge__(self, other):
    3.54  
    3.55          "Return whether this int is greater than or equal to 'other'."
    3.56  
    3.57 -        return _negate(self.__lt__(other))
    3.58 +        return self._binary_op(int_ge, other)
    3.59  
    3.60      def __eq__(self, other):
    3.61  
    3.62 @@ -226,7 +227,7 @@
    3.63  
    3.64          "Return whether this int is not equal to 'other'."
    3.65  
    3.66 -        return _negate(self.__eq__(other))
    3.67 +        return self._binary_op(int_ne, other)
    3.68  
    3.69      def __neg__(self):
    3.70  
     4.1 --- a/optimiser.py	Tue Jul 10 13:28:53 2018 +0200
     4.2 +++ b/optimiser.py	Thu Jul 12 18:19:02 2018 +0200
     4.3 @@ -3,7 +3,7 @@
     4.4  """
     4.5  Optimise object layouts and generate access instruction plans.
     4.6  
     4.7 -Copyright (C) 2014, 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk>
     4.8 +Copyright (C) 2014, 2015, 2016, 2017, 2018 Paul Boddie <paul@boddie.org.uk>
     4.9  
    4.10  This program is free software; you can redistribute it and/or modify it under
    4.11  the terms of the GNU General Public License as published by the Free Software
    4.12 @@ -413,6 +413,12 @@
    4.13          finally:
    4.14              f.close()
    4.15  
    4.16 +    def is_allocated_attribute(self, attrname):
    4.17 +
    4.18 +        "Return whether 'attrname' is to be allocated in an object."
    4.19 +
    4.20 +        return not attrname.startswith("$t")
    4.21 +
    4.22      def populate_objects(self):
    4.23  
    4.24          "Populate objects using attribute and usage information."
    4.25 @@ -432,8 +438,8 @@
    4.26  
    4.27                  # Remove temporary names from structures.
    4.28  
    4.29 -                attrnames = filter(lambda x: not x.startswith("$t"), attrnames)
    4.30 -                self.all_attrs[(objkind, name)] = attrnames
    4.31 +                attrnames = filter(self.is_allocated_attribute, attrnames)
    4.32 +                self.all_attrs[Reference(objkind, name)] = attrnames
    4.33  
    4.34          try:
    4.35              self.locations = get_allocated_locations(self.all_attrs,
    4.36 @@ -495,8 +501,7 @@
    4.37  
    4.38          # Record the structures.
    4.39  
    4.40 -        for (objkind, name), attrnames in self.all_attrs.items():
    4.41 -            key = Reference(objkind, name)
    4.42 +        for key, attrnames in self.all_attrs.items():
    4.43              l = self.structures[key] = [None] * len(attrnames)
    4.44  
    4.45              for attrname in attrnames:
    4.46 @@ -719,7 +724,7 @@
    4.47  
    4.48      """
    4.49      Get attribute and size information for the object attributes defined by 'd'
    4.50 -    providing a mapping from (object kind, type name) to attribute names.
    4.51 +    providing a mapping from object references to attribute names.
    4.52  
    4.53      Return a matrix of attributes (each row entry consisting of column values
    4.54      providing attribute names, with value positions corresponding to types
    4.55 @@ -736,15 +741,15 @@
    4.56      sizes = {}
    4.57      objkinds = {}
    4.58  
    4.59 -    for objtype, attrnames in d.items():
    4.60 -        objkind, _name = objtype
    4.61 +    for ref, attrnames in d.items():
    4.62 +        objkind = ref.get_kind()
    4.63  
    4.64          for attrname in attrnames:
    4.65  
    4.66              # Record each type supporting the attribute.
    4.67  
    4.68              init_item(attrs, attrname, set)
    4.69 -            attrs[attrname].add(objtype)
    4.70 +            attrs[attrname].add(ref)
    4.71  
    4.72              # Maintain a record of the smallest object size supporting the given
    4.73              # attribute.
    4.74 @@ -761,13 +766,13 @@
    4.75  
    4.76      # Obtain attribute details in order of size and occupancy.
    4.77  
    4.78 -    all_objtypes = d.keys()
    4.79 +    all_refs = d.keys()
    4.80  
    4.81      rsizes = []
    4.82      for attrname, size in sizes.items():
    4.83          priority = "<instance>" in objkinds[attrname] and 0.5 or 1
    4.84          occupied = len(attrs[attrname])
    4.85 -        key = (priority * size, size, len(all_objtypes) - occupied, attrname)
    4.86 +        key = (priority * size, size, len(all_refs) - occupied, attrname)
    4.87          rsizes.append(key)
    4.88  
    4.89      rsizes.sort()
    4.90 @@ -775,20 +780,20 @@
    4.91      # Make a matrix of attributes.
    4.92  
    4.93      matrix = {}
    4.94 -    for attrname, objtypes in attrs.items():
    4.95 +    for attrname, refs in attrs.items():
    4.96  
    4.97          # Traverse the object types, adding the attribute name if the object
    4.98          # type supports the attribute, adding None otherwise.
    4.99  
   4.100          row = []
   4.101 -        for objtype in all_objtypes:
   4.102 -            if objtype in objtypes:
   4.103 +        for ref in all_refs:
   4.104 +            if ref in refs:
   4.105                  row.append(attrname)
   4.106              else:
   4.107                  row.append(None)
   4.108          matrix[attrname] = row
   4.109  
   4.110 -    return matrix, all_objtypes, rsizes
   4.111 +    return matrix, all_refs, rsizes
   4.112  
   4.113  def get_parameters_and_sizes(d):
   4.114  
     5.1 --- a/templates/native/common.c	Tue Jul 10 13:28:53 2018 +0200
     5.2 +++ b/templates/native/common.c	Thu Jul 12 18:19:02 2018 +0200
     5.3 @@ -1,6 +1,6 @@
     5.4  /* Common operations for native functions.
     5.5  
     5.6 -Copyright (C) 2016, 2017 Paul Boddie <paul@boddie.org.uk>
     5.7 +Copyright (C) 2016, 2017, 2018 Paul Boddie <paul@boddie.org.uk>
     5.8  
     5.9  This program is free software; you can redistribute it and/or modify it under
    5.10  the terms of the GNU General Public License as published by the Free Software
    5.11 @@ -30,9 +30,9 @@
    5.12  {
    5.13      /* Create a new string and mutate the __data__, __size__ and __key__ attributes. */
    5.14      __attr attr = __NEWINSTANCE(__builtins___str_string);
    5.15 -    attr.value->attrs[__ATTRPOS(__data__)].strvalue = s;
    5.16 -    attr.value->attrs[__ATTRPOS(__size__)] = __INTVALUE(size);
    5.17 -    attr.value->attrs[__ATTRPOS(__key__)] = __NULL;
    5.18 +    __store_via_object(__VALUE(attr), __data__, (__attr) {.strvalue=s});
    5.19 +    __store_via_object(__VALUE(attr), __size__, __INTVALUE(size));
    5.20 +    __store_via_object(__VALUE(attr), __key__, __NULL);
    5.21      return attr;
    5.22  }
    5.23  
    5.24 @@ -40,7 +40,7 @@
    5.25  {
    5.26      /* Create a new list and mutate the __data__ attribute. */
    5.27      __attr attr = __NEWINSTANCE(__builtins___list_list);
    5.28 -    attr.value->attrs[__ATTRPOS(__data__)].seqvalue = f;
    5.29 +    __store_via_object(__VALUE(attr), __data__, (__attr) {.seqvalue=f});
    5.30      return attr;
    5.31  }
    5.32  
     6.1 --- a/templates/ops.c	Tue Jul 10 13:28:53 2018 +0200
     6.2 +++ b/templates/ops.c	Thu Jul 12 18:19:02 2018 +0200
     6.3 @@ -1,6 +1,6 @@
     6.4  /* Common operations.
     6.5  
     6.6 -Copyright (C) 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk>
     6.7 +Copyright (C) 2015, 2016, 2017, 2018 Paul Boddie <paul@boddie.org.uk>
     6.8  
     6.9  This program is free software; you can redistribute it and/or modify it under
    6.10  the terms of the GNU General Public License as published by the Free Software
    6.11 @@ -231,7 +231,7 @@
    6.12  
    6.13  /* Context-related operations. */
    6.14  
    6.15 -int __test_context_update(__attr context, __attr attr)
    6.16 +int __test_context_update(__attr context, __attr attr, int invoke)
    6.17  {
    6.18      /* Return whether the context should be updated for the attribute. */
    6.19  
    6.20 @@ -258,6 +258,11 @@
    6.21              __raise_type_error();
    6.22      }
    6.23  
    6.24 +    /* Without a null or instance context, an invocation cannot be performed. */
    6.25 +
    6.26 +    if (invoke)
    6.27 +        __raise_unbound_method_error();
    6.28 +
    6.29      /* Test for access to a type class attribute using a type instance. */
    6.30  
    6.31      if (__test_specific_type(attrcontextvalue, &__TYPE_CLASS_TYPE) && __is_type_instance(__VALUE(context)))
    6.32 @@ -272,7 +277,7 @@
    6.33  {
    6.34      /* Update the context or return the unchanged attribute. */
    6.35  
    6.36 -    if (__test_context_update(context, attr))
    6.37 +    if (__test_context_update(context, attr, 0))
    6.38          return __update_context(context, attr);
    6.39      else
    6.40          return attr;
    6.41 @@ -288,7 +293,7 @@
    6.42      /* Revert the local context to that employed by the attribute if the
    6.43         supplied context is not appropriate. */
    6.44  
    6.45 -    if (!__test_context_update(context, attr))
    6.46 +    if (!__test_context_update(context, attr, 1))
    6.47          contexts[target] = __CONTEXT_AS_VALUE(attr);
    6.48      return attr;
    6.49  }
    6.50 @@ -297,7 +302,7 @@
    6.51  {
    6.52      /* Set the local context to the specified context if appropriate. */
    6.53  
    6.54 -    if (__test_context_update(context, __ATTRVALUE(value)))
    6.55 +    if (__test_context_update(context, __ATTRVALUE(value), 1))
    6.56          contexts[target] = context;
    6.57      return __ATTRVALUE(value);
    6.58  }
     7.1 --- a/templates/ops.h	Tue Jul 10 13:28:53 2018 +0200
     7.2 +++ b/templates/ops.h	Thu Jul 12 18:19:02 2018 +0200
     7.3 @@ -101,7 +101,7 @@
     7.4  
     7.5  /* Context-related operations. */
     7.6  
     7.7 -int __test_context_update(__attr context, __attr attr);
     7.8 +int __test_context_update(__attr context, __attr attr, int invoke);
     7.9  __attr __test_context(__attr context, __attr attr);
    7.10  __attr __update_context(__attr context, __attr attr);
    7.11  __attr __test_context_revert(int target, __attr context, __attr attr, __attr contexts[]);
     8.1 --- a/templates/progops.c	Tue Jul 10 13:28:53 2018 +0200
     8.2 +++ b/templates/progops.c	Thu Jul 12 18:19:02 2018 +0200
     8.3 @@ -151,6 +151,11 @@
     8.4      __Raise(__new___builtins___core_OverflowError(__NULL));
     8.5  }
     8.6  
     8.7 +void __raise_unbound_method_error()
     8.8 +{
     8.9 +    __Raise(__new___builtins___core_UnboundMethodInvocation(__NULL));
    8.10 +}
    8.11 +
    8.12  void __raise_type_error()
    8.13  {
    8.14      __Raise(__new___builtins___core_TypeError(__NULL));
     9.1 --- a/templates/progops.h	Tue Jul 10 13:28:53 2018 +0200
     9.2 +++ b/templates/progops.h	Thu Jul 12 18:19:02 2018 +0200
     9.3 @@ -1,6 +1,6 @@
     9.4  /* Operations depending on program specifics.
     9.5  
     9.6 -Copyright (C) 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk>
     9.7 +Copyright (C) 2015, 2016, 2017, 2018 Paul Boddie <paul@boddie.org.uk>
     9.8  
     9.9  This program is free software; you can redistribute it and/or modify it under
    9.10  the terms of the GNU General Public License as published by the Free Software
    9.11 @@ -55,6 +55,8 @@
    9.12  
    9.13  void __raise_overflow_error();
    9.14  
    9.15 +void __raise_unbound_method_error();
    9.16 +
    9.17  void __raise_zero_division_error();
    9.18  
    9.19  void __raise_type_error();
    10.1 --- a/tests/methods_rebound.py	Tue Jul 10 13:28:53 2018 +0200
    10.2 +++ b/tests/methods_rebound.py	Thu Jul 12 18:19:02 2018 +0200
    10.3 @@ -13,9 +13,32 @@
    10.4  
    10.5  d = D()
    10.6  
    10.7 +def fn():
    10.8 +    return 456
    10.9 +
   10.10 +class E:
   10.11 +    f = fn
   10.12 +    g = C.f
   10.13 +
   10.14 +e = E()
   10.15 +
   10.16  print c.f.__name__                  # f
   10.17  print c.f()                         # <__main__.C instance>
   10.18                                      # 123
   10.19  print d.f.__name__                  # wrapper
   10.20  print d.f()                         # <__main__.C instance>
   10.21                                      # 123
   10.22 +
   10.23 +print e.f.__name__                  # fn
   10.24 +print e.f()                         # 456
   10.25 +print e.g.__name__                  # f
   10.26 +
   10.27 +try:
   10.28 +    print e.g()
   10.29 +except TypeError:
   10.30 +    print "e.g(): e is an incompatible instance for E.g which is C.f"
   10.31 +
   10.32 +g = get_using(E.g, c)
   10.33 +print g.__name__                    # f
   10.34 +print g()                           # <__main__.C instance>
   10.35 +                                    # 123
    11.1 --- a/translator.py	Tue Jul 10 13:28:53 2018 +0200
    11.2 +++ b/translator.py	Thu Jul 12 18:19:02 2018 +0200
    11.3 @@ -1070,9 +1070,21 @@
    11.4  
    11.5          context_required = True
    11.6          have_access_context = isinstance(expr, AttrResult)
    11.7 +
    11.8 +        # The context identity is merely the thing providing the context.
    11.9 +        # A verified context is one that does not need further testing for
   11.10 +        # suitability.
   11.11 +
   11.12          context_identity = have_access_context and expr.context()
   11.13          context_verified = have_access_context and expr.context_verified()
   11.14 +
   11.15 +        # The presence of any test operations in the accessor expression.
   11.16 +        # With such operations present, the expression cannot be eliminated.
   11.17 +
   11.18          tests_accessor = have_access_context and expr.tests_accessor()
   11.19 +
   11.20 +        # Parameter details and parameter list dimensions.
   11.21 +
   11.22          parameters = None
   11.23          num_parameters = None
   11.24          num_defaults = None
   11.25 @@ -1252,11 +1264,13 @@
   11.26  
   11.27          if context_required:
   11.28              if have_access_context:
   11.29 -                args = [context_identity]
   11.30 +                context_arg = context_identity
   11.31              else:
   11.32 -                args = ["__CONTEXT_AS_VALUE(%s)" % target_var]
   11.33 +                context_arg = "__CONTEXT_AS_VALUE(%s)" % target_var
   11.34          else:
   11.35 -            args = ["__NULL"]
   11.36 +            context_arg = "__NULL"
   11.37 +
   11.38 +        args = [context_arg]
   11.39  
   11.40          # Complete the array with null values, permitting tests for a complete
   11.41          # set of arguments.
   11.42 @@ -1398,25 +1412,23 @@
   11.43          elif function:
   11.44              if context_required:
   11.45  
   11.46 -                # With context_verified or context_identity...
   11.47 -
   11.48 -                if have_access_context:
   11.49 +                # Avoid further context testing if appropriate.
   11.50 +
   11.51 +                if have_access_context and context_verified:
   11.52                      emit("__get_function_member(%s)" % target_expr)
   11.53  
   11.54                  # Otherwise, test the context for the function/method.
   11.55  
   11.56                  else:
   11.57 -                    emit("__get_function(__CONTEXT_AS_VALUE(%s), %s)" % (
   11.58 -                        target_var, target_expr))
   11.59 +                    emit("__get_function(%s, %s)" % (context_arg, target_expr))
   11.60              else:
   11.61                  emit("_get_function_member(%s)" % target_expr)
   11.62  
   11.63          # With known parameters, the target can be tested.
   11.64  
   11.65          elif known_parameters:
   11.66 -            context_arg = context_required and args[0] or "__NULL"
   11.67              if self.always_callable(refs):
   11.68 -                if context_verified or context_identity:
   11.69 +                if context_verified:
   11.70                      emit("__get_function_member(%s)" % target_expr)
   11.71                  else:
   11.72                      emit("__get_function(%s, %s)" % (context_arg, target_expr))