Lichen

Changeset

828:fd110815f7f9
2018-06-24 Paul Boddie raw files shortlog changelog graph Changed target handling in the invocation code to eliminate superfluous temporary storage for targets and to treat context and target storage separately. Changed access instruction generation to expose the accessor test instructions more readily to the translator, with the error conversion being introduced by the translator. This allows the presence of accessor tests to be considered by the translator and for such tests to be included in generated code where they might otherwise be optimised away. Fixed detection of context temporary variables. Simplified the tracking of target and context temporary usage.
deducer.py (file) translator.py (file) transresults.py (file)
     1.1 --- a/deducer.py	Sat Jun 23 18:01:56 2018 +0200
     1.2 +++ b/deducer.py	Sun Jun 24 00:09:33 2018 +0200
     1.3 @@ -2719,7 +2719,7 @@
     1.4              # Apply any test.
     1.5  
     1.6              if test[0] == "test":
     1.7 -                test_accessor = accessor = ("__to_error", ("__%s_%s_%s" % test, accessor, test_type))
     1.8 +                test_accessor = accessor = ("__%s_%s_%s" % test, accessor, test_type)
     1.9              else:
    1.10                  test_accessor = None
    1.11  
     2.1 --- a/translator.py	Sat Jun 23 18:01:56 2018 +0200
     2.2 +++ b/translator.py	Sun Jun 24 00:09:33 2018 +0200
     2.3 @@ -3,7 +3,7 @@
     2.4  """
     2.5  Translate programs.
     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 @@ -25,7 +25,8 @@
    2.13  from encoders import encode_access_instruction, encode_access_instruction_arg, \
    2.14                       encode_function_pointer, encode_literal_instantiator, \
    2.15                       encode_instantiator_pointer, encode_path, encode_symbol, \
    2.16 -                     encode_type_attribute, is_type_attribute
    2.17 +                     encode_type_attribute, is_type_attribute, \
    2.18 +                     type_ops, typename_ops
    2.19  from errors import InspectError, TranslateError
    2.20  from os.path import exists, join
    2.21  from os import makedirs
    2.22 @@ -120,10 +121,6 @@
    2.23          self.in_try_finally = False
    2.24          self.in_try_except = False
    2.25  
    2.26 -        # Invocation adjustments.
    2.27 -
    2.28 -        self.in_argument_list = False
    2.29 -
    2.30          # Attribute access and accessor counting.
    2.31  
    2.32          self.attr_accesses = {}
    2.33 @@ -317,7 +314,9 @@
    2.34          else:
    2.35              self.in_function = False
    2.36              self.function_target = 0
    2.37 -            self.max_function_targets = 0
    2.38 +            self.max_function_target = 0
    2.39 +            self.context_index = 0
    2.40 +            self.max_context_index = 0
    2.41              self.start_module()
    2.42              self.process_structure(node)
    2.43              self.end_module()
    2.44 @@ -555,10 +554,11 @@
    2.45          # The context set or retrieved will be that used by any enclosing
    2.46          # invocation.
    2.47  
    2.48 -        context_index = self.function_target - 1
    2.49 +        context_index = self.context_index
    2.50          context_identity = None
    2.51          context_identity_verified = False
    2.52          final_identity = None
    2.53 +        accessor_test = False
    2.54  
    2.55          # Obtain encoded versions of each instruction, accumulating temporary
    2.56          # variables.
    2.57 @@ -579,6 +579,12 @@
    2.58                  final_identity = instruction[1]
    2.59                  continue
    2.60  
    2.61 +            # Modify test instructions.
    2.62 +
    2.63 +            elif instruction[0] in typename_ops or instruction[0] in type_ops:
    2.64 +                instruction = ("__to_error", instruction)
    2.65 +                accessor_test = True
    2.66 +
    2.67              # Collect the encoded instruction, noting any temporary variables
    2.68              # required by it.
    2.69  
    2.70 @@ -599,7 +605,7 @@
    2.71              refs = [ref]
    2.72  
    2.73          del self.attrs[0]
    2.74 -        return AttrResult(output, refs, location, context_identity, context_identity_verified)
    2.75 +        return AttrResult(output, refs, location, context_identity, context_identity_verified, accessor_test)
    2.76  
    2.77      def init_substitutions(self):
    2.78  
    2.79 @@ -837,7 +843,9 @@
    2.80          in_conditional = self.in_conditional
    2.81          self.in_conditional = False
    2.82          self.function_target = 0
    2.83 -        self.max_function_targets = 0
    2.84 +        self.max_function_target = 0
    2.85 +        self.context_index = 0
    2.86 +        self.max_context_index = 0
    2.87  
    2.88          # Volatile locals for exception handling.
    2.89  
    2.90 @@ -1040,23 +1048,10 @@
    2.91  
    2.92          "Process the given invocation node 'n'."
    2.93  
    2.94 -        # Any invocations in the expression will store target details in a
    2.95 -        # different location.
    2.96 -
    2.97 -        self.next_target()
    2.98 -
    2.99 -        in_argument_list = self.in_argument_list
   2.100 -        self.in_argument_list = False
   2.101 -
   2.102          # Process the expression.
   2.103  
   2.104          expr = self.process_structure_node(n.node)
   2.105  
   2.106 -        # Reference the current target again.
   2.107 -
   2.108 -        self.in_argument_list = in_argument_list
   2.109 -        self.function_target -= 1
   2.110 -
   2.111          # Obtain details of the invocation expression.
   2.112  
   2.113          objpath = expr.get_origin()
   2.114 @@ -1083,6 +1078,7 @@
   2.115          have_access_context = isinstance(expr, AttrResult)
   2.116          context_identity = have_access_context and expr.context()
   2.117          context_verified = have_access_context and expr.context_verified()
   2.118 +        tests_accessor = have_access_context and expr.tests_accessor()
   2.119          parameters = None
   2.120          num_parameters = None
   2.121          num_defaults = None
   2.122 @@ -1204,28 +1200,61 @@
   2.123                              len(self.importer.function_parameters[_objpath]),
   2.124                              _objpath)
   2.125  
   2.126 +        # Logical statement about available parameter information.
   2.127 +
   2.128 +        known_parameters = num_parameters is not None
   2.129 +
   2.130 +        # The source of context information: target or temporary.
   2.131 +
   2.132 +        need_context_target = context_required and not have_access_context
   2.133 +
   2.134 +        need_context_stored = context_required and context_identity and \
   2.135 +                              context_identity.startswith("__get_context")
   2.136 +
   2.137          # Determine any readily-accessible target identity.
   2.138  
   2.139          target_named = expr.is_name() and str(expr) or None
   2.140 +        target_identity = target or target_named
   2.141 +
   2.142 +        # Use of target information to populate defaults.
   2.143 +
   2.144 +        defaults_target_var = not (parameters and function_defaults is not None) and \
   2.145 +                              known_parameters and len(n.args) < num_parameters
   2.146 +
   2.147 +        # Use of a temporary target variable in these situations:
   2.148 +        #
   2.149 +        # A target provided by an expression needed for defaults.
   2.150 +        #
   2.151 +        # A target providing the context but not using a name to do so.
   2.152 +        #
   2.153 +        # A target expression involving the definition of a context which may
   2.154 +        # then be evaluated and stored to ensure that the context is available
   2.155 +        # during argument evaluation.
   2.156 +        #
   2.157 +        # An expression featuring an accessor test.
   2.158 +
   2.159 +        need_target_stored = defaults_target_var and not target_identity or \
   2.160 +                             need_context_target and not target_named or \
   2.161 +                             need_context_stored or \
   2.162 +                             tests_accessor and not target
   2.163 +
   2.164 +        # Define stored target details.
   2.165 +
   2.166          target_stored = "__tmp_targets[%d]" % self.function_target
   2.167  
   2.168 -        target_identity = target or target_named
   2.169 -        target_var = target_identity or target_stored
   2.170 -        context_var = target_named or target_stored
   2.171 -
   2.172 -        if not target_identity:
   2.173 +        target_var = need_target_stored and target_stored or target_identity
   2.174 +        context_var = need_target_stored and target_stored or target_named
   2.175 +
   2.176 +        if need_target_stored:
   2.177              self.record_temp("__tmp_targets")
   2.178  
   2.179 -        if context_identity:
   2.180 -            if context_identity.startswith("__tmp_contexts"):
   2.181 -                self.record_temp("__tmp_contexts")
   2.182 +        if need_context_stored:
   2.183 +            self.record_temp("__tmp_contexts")
   2.184  
   2.185          # Arguments are presented in a temporary frame array with any context
   2.186          # always being the first argument. Where it would be unused, it may be
   2.187          # set to null.
   2.188  
   2.189 -        known_parameters = num_parameters is not None
   2.190 -
   2.191          if context_required:
   2.192              if have_access_context:
   2.193                  args = [context_identity]
   2.194 @@ -1245,12 +1274,13 @@
   2.195          # different location.
   2.196  
   2.197          function_target = self.function_target
   2.198 -
   2.199 -        if not target_identity:
   2.200 +        context_index = self.context_index
   2.201 +
   2.202 +        if need_target_stored:
   2.203              self.next_target()
   2.204  
   2.205 -        in_argument_list = self.in_argument_list
   2.206 -        self.in_argument_list = True
   2.207 +        if need_context_stored:
   2.208 +            self.next_context()
   2.209  
   2.210          for i, arg in enumerate(n.args):
   2.211              argexpr = self.process_structure_node(arg)
   2.212 @@ -1290,10 +1320,8 @@
   2.213  
   2.214          # Reference the current target again.
   2.215  
   2.216 -        self.in_argument_list = in_argument_list
   2.217 -
   2.218 -        if not self.in_argument_list:
   2.219 -            self.function_target = function_target
   2.220 +        self.function_target = function_target
   2.221 +        self.context_index = context_index
   2.222  
   2.223          # Defaults are added to the frame where arguments are missing.
   2.224  
   2.225 @@ -1343,27 +1371,31 @@
   2.226          # First, the invocation expression is presented.
   2.227  
   2.228          stages = []
   2.229 -
   2.230 -        # Without a known specific callable, the expression provides the target.
   2.231 -
   2.232 -        if not target or context_required:
   2.233 -
   2.234 -            # The context is set in the expression.
   2.235 -
   2.236 -            if target and not target_named:
   2.237 -
   2.238 -                # Test whether the expression provides anything.
   2.239 -
   2.240 -                if expr:
   2.241 -                    stages.append(str(expr))
   2.242 -
   2.243 -            elif not target_identity:
   2.244 -                stages.append("%s = %s" % (target_var, expr))
   2.245 +        emit = stages.append
   2.246 +
   2.247 +        # Assign and yield any stored target.
   2.248 +        # The context may be set in the expression.
   2.249 +
   2.250 +        if need_target_stored:
   2.251 +            emit("%s = %s" % (target_var, expr))
   2.252 +            target_expr = target_var
   2.253 +
   2.254 +        # Otherwise, retain the expression for later use.
   2.255 +
   2.256 +        else:
   2.257 +            target_expr = str(expr)
   2.258  
   2.259          # Any specific callable is then obtained for invocation.
   2.260  
   2.261          if target:
   2.262 -            stages.append(target)
   2.263 +
   2.264 +            # An expression involving a test of the accessor providing the target.
   2.265 +            # This must be emitted in order to perform the test.
   2.266 +
   2.267 +            if tests_accessor:
   2.268 +                emit(str(expr))
   2.269 +
   2.270 +            emit(target)
   2.271  
   2.272          # Methods accessed via unidentified accessors are obtained for
   2.273          # invocation.
   2.274 @@ -1372,15 +1404,15 @@
   2.275              if context_required:
   2.276                  if have_access_context:
   2.277                      if context_verified:
   2.278 -                        stages.append("__get_function_member(%s)" % target_var)
   2.279 +                        emit("__get_function_member(%s)" % target_expr)
   2.280                      else:
   2.281 -                        stages.append("__get_function(%s, %s)" % (
   2.282 -                            context_identity, target_var))
   2.283 +                        emit("__get_function(%s, %s)" % (
   2.284 +                            context_identity, target_expr))
   2.285                  else:
   2.286 -                    stages.append("__get_function(__CONTEXT_AS_VALUE(%s), %s)" % (
   2.287 -                        context_var, target_var))
   2.288 +                    emit("__get_function(__CONTEXT_AS_VALUE(%s), %s)" % (
   2.289 +                        context_var, target_expr))
   2.290              else:
   2.291 -                stages.append("_get_function_member(%s)" % target_var)
   2.292 +                emit("_get_function_member(%s)" % target_expr)
   2.293  
   2.294          # With known parameters, the target can be tested.
   2.295  
   2.296 @@ -1388,11 +1420,11 @@
   2.297              context_arg = context_required and args[0] or "__NULL"
   2.298              if self.always_callable(refs):
   2.299                  if context_verified:
   2.300 -                    stages.append("__get_function_member(%s)" % target_var)
   2.301 +                    emit("__get_function_member(%s)" % target_expr)
   2.302                  else:
   2.303 -                    stages.append("__get_function(%s, %s)" % (context_arg, target_var))
   2.304 +                    emit("__get_function(%s, %s)" % (context_arg, target_expr))
   2.305              else:
   2.306 -                stages.append("__check_and_get_function(%s, %s)" % (context_arg, target_var))
   2.307 +                emit("__check_and_get_function(%s, %s)" % (context_arg, target_expr))
   2.308  
   2.309          # With a known target, the function is obtained directly and called.
   2.310          # By putting the invocation at the end of the final element in the
   2.311 @@ -1411,8 +1443,8 @@
   2.312          # the callable and argument collections.
   2.313  
   2.314          else:
   2.315 -            stages.append("__invoke(\n%s,\n%d, %d, %s, %s,\n%d, %s\n)" % (
   2.316 -                target_var,
   2.317 +            emit("__invoke(\n%s,\n%d, %d, %s, %s,\n%d, %s\n)" % (
   2.318 +                target_expr,
   2.319                  self.always_callable(refs) and 1 or 0,
   2.320                  len(kwargs), kwcodestr, kwargstr,
   2.321                  len(args), "__ARGS(%s)" % argstr))
   2.322 @@ -1423,7 +1455,14 @@
   2.323          "Allocate the next function target storage."
   2.324  
   2.325          self.function_target += 1
   2.326 -        self.max_function_targets = max(self.function_target, self.max_function_targets)
   2.327 +        self.max_function_target = max(self.function_target, self.max_function_target)
   2.328 +
   2.329 +    def next_context(self):
   2.330 +
   2.331 +        "Allocate the next context value storage."
   2.332 +
   2.333 +        self.context_index += 1
   2.334 +        self.max_context_index = max(self.context_index, self.max_context_index)
   2.335  
   2.336      def always_callable(self, refs):
   2.337  
   2.338 @@ -2061,12 +2100,15 @@
   2.339  
   2.340          # Provide space for the given number of targets.
   2.341  
   2.342 -        targets = self.max_function_targets
   2.343 +        targets = self.max_function_target
   2.344  
   2.345          if self.uses_temp(name, "__tmp_targets"):
   2.346              self.writeline("__attr __tmp_targets[%d];" % targets)
   2.347 +
   2.348 +        index = self.max_context_index
   2.349 +
   2.350          if self.uses_temp(name, "__tmp_contexts"):
   2.351 -            self.writeline("__attr __tmp_contexts[%d];" % targets)
   2.352 +            self.writeline("__attr __tmp_contexts[%d];" % index)
   2.353  
   2.354          # Add temporary variable usage details.
   2.355  
     3.1 --- a/transresults.py	Sat Jun 23 18:01:56 2018 +0200
     3.2 +++ b/transresults.py	Sun Jun 24 00:09:33 2018 +0200
     3.3 @@ -178,12 +178,14 @@
     3.4  
     3.5      "A translation result for an attribute access."
     3.6  
     3.7 -    def __init__(self, instructions, refs, location, context_identity, context_identity_verified):
     3.8 +    def __init__(self, instructions, refs, location, context_identity,
     3.9 +                 context_identity_verified, accessor_test):
    3.10          InstructionSequence.__init__(self, instructions)
    3.11          self.refs = refs
    3.12          self.location = location
    3.13          self.context_identity = context_identity
    3.14          self.context_identity_verified = context_identity_verified
    3.15 +        self.accessor_test = accessor_test
    3.16  
    3.17      def references(self):
    3.18          return self.refs
    3.19 @@ -197,6 +199,9 @@
    3.20      def context_verified(self):
    3.21          return self.context_identity_verified and self.context() or None
    3.22  
    3.23 +    def tests_accessor(self):
    3.24 +        return self.accessor_test
    3.25 +
    3.26      def get_origin(self):
    3.27          return self.refs and len(self.refs) == 1 and first(self.refs).get_origin()
    3.28  
    3.29 @@ -215,7 +220,8 @@
    3.30          return encode_instructions(self.instructions)
    3.31  
    3.32      def __repr__(self):
    3.33 -        return "AttrResult(%r, %r, %r, %r)" % (self.instructions, self.refs, self.location, self.context_identity)
    3.34 +        return "AttrResult(%r, %r, %r, %r, %r)" % (self.instructions, self.refs,
    3.35 +               self.location, self.context_identity, self.accessor_test)
    3.36  
    3.37  class AliasResult(NameRef, Result):
    3.38