# HG changeset patch # User Paul Boddie # Date 1529791773 -7200 # Node ID fd110815f7f9d341fdf4fd163216389406bd35bb # Parent 57652d9c6f246a352b364ee88f8bf76cef12eaab 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. diff -r 57652d9c6f24 -r fd110815f7f9 deducer.py --- a/deducer.py Sat Jun 23 18:01:56 2018 +0200 +++ b/deducer.py Sun Jun 24 00:09:33 2018 +0200 @@ -2719,7 +2719,7 @@ # Apply any test. if test[0] == "test": - test_accessor = accessor = ("__to_error", ("__%s_%s_%s" % test, accessor, test_type)) + test_accessor = accessor = ("__%s_%s_%s" % test, accessor, test_type) else: test_accessor = None diff -r 57652d9c6f24 -r fd110815f7f9 translator.py --- a/translator.py Sat Jun 23 18:01:56 2018 +0200 +++ b/translator.py Sun Jun 24 00:09:33 2018 +0200 @@ -3,7 +3,7 @@ """ Translate programs. -Copyright (C) 2015, 2016, 2017 Paul Boddie +Copyright (C) 2015, 2016, 2017, 2018 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -25,7 +25,8 @@ from encoders import encode_access_instruction, encode_access_instruction_arg, \ encode_function_pointer, encode_literal_instantiator, \ encode_instantiator_pointer, encode_path, encode_symbol, \ - encode_type_attribute, is_type_attribute + encode_type_attribute, is_type_attribute, \ + type_ops, typename_ops from errors import InspectError, TranslateError from os.path import exists, join from os import makedirs @@ -120,10 +121,6 @@ self.in_try_finally = False self.in_try_except = False - # Invocation adjustments. - - self.in_argument_list = False - # Attribute access and accessor counting. self.attr_accesses = {} @@ -317,7 +314,9 @@ else: self.in_function = False self.function_target = 0 - self.max_function_targets = 0 + self.max_function_target = 0 + self.context_index = 0 + self.max_context_index = 0 self.start_module() self.process_structure(node) self.end_module() @@ -555,10 +554,11 @@ # The context set or retrieved will be that used by any enclosing # invocation. - context_index = self.function_target - 1 + context_index = self.context_index context_identity = None context_identity_verified = False final_identity = None + accessor_test = False # Obtain encoded versions of each instruction, accumulating temporary # variables. @@ -579,6 +579,12 @@ final_identity = instruction[1] continue + # Modify test instructions. + + elif instruction[0] in typename_ops or instruction[0] in type_ops: + instruction = ("__to_error", instruction) + accessor_test = True + # Collect the encoded instruction, noting any temporary variables # required by it. @@ -599,7 +605,7 @@ refs = [ref] del self.attrs[0] - return AttrResult(output, refs, location, context_identity, context_identity_verified) + return AttrResult(output, refs, location, context_identity, context_identity_verified, accessor_test) def init_substitutions(self): @@ -837,7 +843,9 @@ in_conditional = self.in_conditional self.in_conditional = False self.function_target = 0 - self.max_function_targets = 0 + self.max_function_target = 0 + self.context_index = 0 + self.max_context_index = 0 # Volatile locals for exception handling. @@ -1040,23 +1048,10 @@ "Process the given invocation node 'n'." - # Any invocations in the expression will store target details in a - # different location. - - self.next_target() - - in_argument_list = self.in_argument_list - self.in_argument_list = False - # Process the expression. expr = self.process_structure_node(n.node) - # Reference the current target again. - - self.in_argument_list = in_argument_list - self.function_target -= 1 - # Obtain details of the invocation expression. objpath = expr.get_origin() @@ -1083,6 +1078,7 @@ have_access_context = isinstance(expr, AttrResult) context_identity = have_access_context and expr.context() context_verified = have_access_context and expr.context_verified() + tests_accessor = have_access_context and expr.tests_accessor() parameters = None num_parameters = None num_defaults = None @@ -1204,28 +1200,61 @@ len(self.importer.function_parameters[_objpath]), _objpath) + # Logical statement about available parameter information. + + known_parameters = num_parameters is not None + + # The source of context information: target or temporary. + + need_context_target = context_required and not have_access_context + + need_context_stored = context_required and context_identity and \ + context_identity.startswith("__get_context") + # Determine any readily-accessible target identity. target_named = expr.is_name() and str(expr) or None + target_identity = target or target_named + + # Use of target information to populate defaults. + + defaults_target_var = not (parameters and function_defaults is not None) and \ + known_parameters and len(n.args) < num_parameters + + # Use of a temporary target variable in these situations: + # + # A target provided by an expression needed for defaults. + # + # A target providing the context but not using a name to do so. + # + # A target expression involving the definition of a context which may + # then be evaluated and stored to ensure that the context is available + # during argument evaluation. + # + # An expression featuring an accessor test. + + need_target_stored = defaults_target_var and not target_identity or \ + need_context_target and not target_named or \ + need_context_stored or \ + tests_accessor and not target + + # Define stored target details. + target_stored = "__tmp_targets[%d]" % self.function_target - target_identity = target or target_named - target_var = target_identity or target_stored - context_var = target_named or target_stored - - if not target_identity: + target_var = need_target_stored and target_stored or target_identity + context_var = need_target_stored and target_stored or target_named + + if need_target_stored: self.record_temp("__tmp_targets") - if context_identity: - if context_identity.startswith("__tmp_contexts"): - self.record_temp("__tmp_contexts") + if need_context_stored: + self.record_temp("__tmp_contexts") # Arguments are presented in a temporary frame array with any context # always being the first argument. Where it would be unused, it may be # set to null. - known_parameters = num_parameters is not None - if context_required: if have_access_context: args = [context_identity] @@ -1245,12 +1274,13 @@ # different location. function_target = self.function_target - - if not target_identity: + context_index = self.context_index + + if need_target_stored: self.next_target() - in_argument_list = self.in_argument_list - self.in_argument_list = True + if need_context_stored: + self.next_context() for i, arg in enumerate(n.args): argexpr = self.process_structure_node(arg) @@ -1290,10 +1320,8 @@ # Reference the current target again. - self.in_argument_list = in_argument_list - - if not self.in_argument_list: - self.function_target = function_target + self.function_target = function_target + self.context_index = context_index # Defaults are added to the frame where arguments are missing. @@ -1343,27 +1371,31 @@ # First, the invocation expression is presented. stages = [] - - # Without a known specific callable, the expression provides the target. - - if not target or context_required: - - # The context is set in the expression. - - if target and not target_named: - - # Test whether the expression provides anything. - - if expr: - stages.append(str(expr)) - - elif not target_identity: - stages.append("%s = %s" % (target_var, expr)) + emit = stages.append + + # Assign and yield any stored target. + # The context may be set in the expression. + + if need_target_stored: + emit("%s = %s" % (target_var, expr)) + target_expr = target_var + + # Otherwise, retain the expression for later use. + + else: + target_expr = str(expr) # Any specific callable is then obtained for invocation. if target: - stages.append(target) + + # An expression involving a test of the accessor providing the target. + # This must be emitted in order to perform the test. + + if tests_accessor: + emit(str(expr)) + + emit(target) # Methods accessed via unidentified accessors are obtained for # invocation. @@ -1372,15 +1404,15 @@ if context_required: if have_access_context: if context_verified: - stages.append("__get_function_member(%s)" % target_var) + emit("__get_function_member(%s)" % target_expr) else: - stages.append("__get_function(%s, %s)" % ( - context_identity, target_var)) + emit("__get_function(%s, %s)" % ( + context_identity, target_expr)) else: - stages.append("__get_function(__CONTEXT_AS_VALUE(%s), %s)" % ( - context_var, target_var)) + emit("__get_function(__CONTEXT_AS_VALUE(%s), %s)" % ( + context_var, target_expr)) else: - stages.append("_get_function_member(%s)" % target_var) + emit("_get_function_member(%s)" % target_expr) # With known parameters, the target can be tested. @@ -1388,11 +1420,11 @@ context_arg = context_required and args[0] or "__NULL" if self.always_callable(refs): if context_verified: - stages.append("__get_function_member(%s)" % target_var) + emit("__get_function_member(%s)" % target_expr) else: - stages.append("__get_function(%s, %s)" % (context_arg, target_var)) + emit("__get_function(%s, %s)" % (context_arg, target_expr)) else: - stages.append("__check_and_get_function(%s, %s)" % (context_arg, target_var)) + emit("__check_and_get_function(%s, %s)" % (context_arg, target_expr)) # With a known target, the function is obtained directly and called. # By putting the invocation at the end of the final element in the @@ -1411,8 +1443,8 @@ # the callable and argument collections. else: - stages.append("__invoke(\n%s,\n%d, %d, %s, %s,\n%d, %s\n)" % ( - target_var, + emit("__invoke(\n%s,\n%d, %d, %s, %s,\n%d, %s\n)" % ( + target_expr, self.always_callable(refs) and 1 or 0, len(kwargs), kwcodestr, kwargstr, len(args), "__ARGS(%s)" % argstr)) @@ -1423,7 +1455,14 @@ "Allocate the next function target storage." self.function_target += 1 - self.max_function_targets = max(self.function_target, self.max_function_targets) + self.max_function_target = max(self.function_target, self.max_function_target) + + def next_context(self): + + "Allocate the next context value storage." + + self.context_index += 1 + self.max_context_index = max(self.context_index, self.max_context_index) def always_callable(self, refs): @@ -2061,12 +2100,15 @@ # Provide space for the given number of targets. - targets = self.max_function_targets + targets = self.max_function_target if self.uses_temp(name, "__tmp_targets"): self.writeline("__attr __tmp_targets[%d];" % targets) + + index = self.max_context_index + if self.uses_temp(name, "__tmp_contexts"): - self.writeline("__attr __tmp_contexts[%d];" % targets) + self.writeline("__attr __tmp_contexts[%d];" % index) # Add temporary variable usage details. diff -r 57652d9c6f24 -r fd110815f7f9 transresults.py --- a/transresults.py Sat Jun 23 18:01:56 2018 +0200 +++ b/transresults.py Sun Jun 24 00:09:33 2018 +0200 @@ -178,12 +178,14 @@ "A translation result for an attribute access." - def __init__(self, instructions, refs, location, context_identity, context_identity_verified): + def __init__(self, instructions, refs, location, context_identity, + context_identity_verified, accessor_test): InstructionSequence.__init__(self, instructions) self.refs = refs self.location = location self.context_identity = context_identity self.context_identity_verified = context_identity_verified + self.accessor_test = accessor_test def references(self): return self.refs @@ -197,6 +199,9 @@ def context_verified(self): return self.context_identity_verified and self.context() or None + def tests_accessor(self): + return self.accessor_test + def get_origin(self): return self.refs and len(self.refs) == 1 and first(self.refs).get_origin() @@ -215,7 +220,8 @@ return encode_instructions(self.instructions) def __repr__(self): - return "AttrResult(%r, %r, %r, %r)" % (self.instructions, self.refs, self.location, self.context_identity) + return "AttrResult(%r, %r, %r, %r, %r)" % (self.instructions, self.refs, + self.location, self.context_identity, self.accessor_test) class AliasResult(NameRef, Result):