# HG changeset patch # User Paul Boddie # Date 1532207966 -7200 # Node ID 8f8361de472ae4b511cd393f7a8e6fe400eddca0 # Parent 51ba0ae74af25f647ec815b4d8dc700f7fb2a1dd Invocations in parameter lists may require temporary storage for contexts and accessors, but the storage locations must be different. Otherwise, the code will generate a sequence-point warning. Here, distinct ranges for parameters in lists are introduced, and accessors are now also stored in arrays, permitting distinct storage. diff -r 51ba0ae74af2 -r 8f8361de472a encoders.py --- a/encoders.py Mon Jul 16 23:00:47 2018 +0200 +++ b/encoders.py Sat Jul 21 23:19:26 2018 +0200 @@ -259,6 +259,14 @@ "__load_static_ignore", "__load_static_replace", "__load_static_test", "", ) +accessor_values = ( + "", + ) + +accessor_ops = ( + "", "", + ) + context_values = ( "", ) @@ -278,7 +286,7 @@ "", "", "", "", "" ) -def encode_access_instruction(instruction, subs, context_index): +def encode_access_instruction(instruction, subs, accessor_index, context_index): """ Encode the 'instruction' - a sequence starting with an operation and @@ -288,6 +296,9 @@ The 'subs' parameter defines a mapping of substitutions for special values used in instructions. + The 'accessor_index' parameter defines the position in local accessor + storage for the referenced accessor or affected by an accessor operation. + The 'context_index' parameter defines the position in local context storage for the referenced context or affected by a context operation. @@ -304,7 +315,7 @@ if args: converting_op = op for arg in args: - s, _substituted = encode_access_instruction_arg(arg, subs, converting_op, context_index) + s, _substituted = encode_access_instruction_arg(arg, subs, converting_op, accessor_index, context_index) substituted.update(_substituted) a.append(s) converting_op = None @@ -326,6 +337,11 @@ elif op in static_ops: a[-1] = "&%s" % a[-1] + # Add accessor storage information to certain operations. + + if op in accessor_ops: + a.insert(0, accessor_index) + # Add context storage information to certain operations. if op in context_ops: @@ -365,19 +381,19 @@ return "%s%s" % (op, argstr), substituted -def encode_access_instruction_arg(arg, subs, op, context_index): +def encode_access_instruction_arg(arg, subs, op, accessor_index, context_index): """ Encode 'arg' using 'subs' to define substitutions, 'op' to indicate the - operation to which the argument belongs, and 'context_index' to indicate any - affected context storage. + operation to which the argument belongs, and with 'accessor_index' and + 'context_index' indicating any affected accessor and context storage. Return a tuple containing the encoded form of 'arg' along with a collection of any substituted values. """ if isinstance(arg, tuple): - encoded, substituted = encode_access_instruction(arg, subs, context_index) + encoded, substituted = encode_access_instruction(arg, subs, accessor_index, context_index) return attribute_to_reference(op, arg[0], encoded, substituted) # Special values only need replacing, not encoding. @@ -386,7 +402,7 @@ # Handle values modified by storage details. - if arg in context_values: + if arg in accessor_values or arg in context_values: encoded = "%s(%s)" % (subs.get(arg), context_index) else: encoded = subs.get(arg) diff -r 51ba0ae74af2 -r 8f8361de472a templates/ops.h --- a/templates/ops.h Mon Jul 16 23:00:47 2018 +0200 +++ b/templates/ops.h Sat Jul 21 23:19:26 2018 +0200 @@ -107,10 +107,11 @@ __attr __test_context_revert(int target, __attr context, __attr attr, __attr contexts[]); __attr __test_context_static(int target, __attr context, __ref value, __attr contexts[]); +#define __get_accessor(__TARGET) (__tmp_values[__TARGET]) #define __get_context(__TARGET) (__tmp_contexts[__TARGET]) #define __set_context(__TARGET, __ATTR) (__tmp_contexts[__TARGET] = (__ATTR)) #define __set_private_context(__ATTR) (__tmp_private_context = (__ATTR)) -#define __set_accessor(__ATTR) (__tmp_value = (__ATTR)) +#define __set_accessor(__TARGET, __ATTR) (__tmp_values[__TARGET] = (__ATTR)) #define __set_target_accessor(__ATTR) (__tmp_target_value = (__ATTR)) /* Context testing for invocations. */ diff -r 51ba0ae74af2 -r 8f8361de472a tests/nested_calls.py --- a/tests/nested_calls.py Mon Jul 16 23:00:47 2018 +0200 +++ b/tests/nested_calls.py Sat Jul 21 23:19:26 2018 +0200 @@ -13,3 +13,31 @@ c = C(3) print c.length() # 6 + +# Explicit function for addition purposes. + +def combine(x, y, z): + return x + y + z + +class Tree: + def __init__(self, item, left=None, right=None): + self.item = item + self.left = left + self.right = right + + def count(self): + if self.left and self.right: + # Test calls in parameter lists needing separate temporary storage. + return combine(self.item, self.left.count(), self.right.count()) + else: + return self.item + +tree = \ + Tree(10000, + Tree(2000, + Tree(300), + Tree(40) + ), + Tree(5)) + +print tree.count() # 12345 diff -r 51ba0ae74af2 -r 8f8361de472a translator.py --- a/translator.py Mon Jul 16 23:00:47 2018 +0200 +++ b/translator.py Sat Jul 21 23:19:26 2018 +0200 @@ -115,6 +115,7 @@ self.namespaces = [] self.in_conditional = False + self.in_parameter_list = False # Exception raising adjustments. @@ -317,6 +318,8 @@ self.max_function_target = 0 self.context_index = 0 self.max_context_index = 0 + self.accessor_index = 0 + self.max_accessor_index = 0 self.start_module() self.process_structure(node) self.end_module() @@ -554,11 +557,13 @@ # The context set or retrieved will be that used by any enclosing # invocation. + accessor_index = self.accessor_index context_index = self.context_index context_identity = None context_identity_verified = False final_identity = None accessor_test = False + accessor_stored = False # Obtain encoded versions of each instruction, accumulating temporary # variables. @@ -568,7 +573,9 @@ # Intercept a special instruction identifying the context. if instruction[0] in ("", ""): - context_identity, _substituted = encode_access_instruction_arg(instruction[1], subs, instruction[0], context_index) + context_identity, _substituted = \ + encode_access_instruction_arg(instruction[1], subs, instruction[0], + accessor_index, context_index) context_identity_verified = instruction[0] == "" continue @@ -585,10 +592,16 @@ instruction = ("__to_error", instruction) accessor_test = True + # Intercept accessor storage. + + elif instruction[0] == "": + accessor_stored = True + # Collect the encoded instruction, noting any temporary variables # required by it. - encoded, _substituted = encode_access_instruction(instruction, subs, context_index) + encoded, _substituted = encode_access_instruction(instruction, subs, + accessor_index, context_index) output.append(encoded) substituted.update(_substituted) @@ -604,7 +617,9 @@ refs = set([self.importer.identify(final_identity)]) del self.attrs[0] - return AttrResult(output, refs, location, context_identity, context_identity_verified, accessor_test) + return AttrResult(output, refs, location, + context_identity, context_identity_verified, + accessor_test, accessor_stored) def init_substitutions(self): @@ -619,21 +634,22 @@ # Substitutions used by instructions. "" : "__tmp_private_context", - "" : "__tmp_value", "" : "__tmp_target_value", # Mappings to be replaced by those given below. + "" : "__tmp_values", "" : "__tmp_contexts", "" : "__tmp_contexts", "" : "__tmp_contexts", "" : "__tmp_contexts", "" : "__tmp_private_context", - "" : "__tmp_value", + "" : "__tmp_values", "" : "__tmp_target_value", } self.op_subs = { + "" : "__get_accessor", "" : "__get_context", "" : "__test_context_revert", "" : "__test_context_static", @@ -840,6 +856,8 @@ self.max_function_target = 0 self.context_index = 0 self.max_context_index = 0 + self.accessor_index = 0 + self.max_accessor_index = 0 # Volatile locals for exception handling. @@ -1082,6 +1100,7 @@ # With such operations present, the expression cannot be eliminated. tests_accessor = have_access_context and expr.tests_accessor() + stores_accessor = have_access_context and expr.stores_accessor() # Parameter details and parameter list dimensions. @@ -1258,6 +1277,9 @@ if need_context_stored: self.record_temp("__tmp_contexts") + if stores_accessor: + self.record_temp("__tmp_values") + # 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. @@ -1284,6 +1306,7 @@ function_target = self.function_target context_index = self.context_index + accessor_index = self.accessor_index if need_target_stored: self.next_target() @@ -1291,6 +1314,12 @@ if need_context_stored: self.next_context() + if stores_accessor: + self.next_accessor() + + in_parameter_list = self.in_parameter_list + self.in_parameter_list = True + for i, arg in enumerate(n.args): argexpr = self.process_structure_node(arg) @@ -1329,8 +1358,12 @@ # Reference the current target again. - self.function_target = function_target - self.context_index = context_index + self.in_parameter_list = in_parameter_list + + if not self.in_parameter_list: + self.function_target = function_target + self.context_index = context_index + self.accessor_index = accessor_index # Defaults are added to the frame where arguments are missing. @@ -1473,6 +1506,13 @@ self.context_index += 1 self.max_context_index = max(self.context_index, self.max_context_index) + def next_accessor(self): + + "Allocate the next accessor value storage." + + self.accessor_index += 1 + self.max_accessor_index = max(self.accessor_index, self.max_accessor_index) + def always_callable(self, refs): "Determine whether all 'refs' are callable." @@ -1527,8 +1567,9 @@ name = self.get_lambda_name() function_name = self.get_object_path(name) - - defaults = self.process_function_defaults(n, name, function_name, "__tmp_value") + instance_name = "__get_accessor(%d)" % self.accessor_index + + defaults = self.process_function_defaults(n, name, function_name, instance_name) # Without defaults, produce an attribute referring to the function. @@ -1539,11 +1580,16 @@ # copy. else: - self.record_temp("__tmp_value") - return make_expression("(__tmp_value = __ATTRVALUE(__COPY(&%s, sizeof(%s))), %s, __tmp_value)" % ( + self.record_temp("__tmp_values") + return make_expression("""\ +(__set_accessor(%d, __ATTRVALUE(__COPY(&%s, sizeof(%s)))), + %s, + __get_accessor(%d))""" % ( + self.accessor_index, encode_path(function_name), encode_symbol("obj", function_name), - ", ".join(defaults))) + ", ".join(defaults), + self.accessor_index)) def process_logical_node(self, n): @@ -2119,24 +2165,21 @@ "Write temporary storage employed by 'name'." - # Provide space for the given number of targets. - - targets = self.max_function_target + # Provide space for the recorded number of temporary variables. if self.uses_temp(name, "__tmp_targets"): - self.writeline("__attr __tmp_targets[%d];" % targets) - - index = self.max_context_index + self.writeline("__attr __tmp_targets[%d];" % self.max_function_target) if self.uses_temp(name, "__tmp_contexts"): - self.writeline("__attr __tmp_contexts[%d];" % index) + self.writeline("__attr __tmp_contexts[%d];" % self.max_context_index) + + if self.uses_temp(name, "__tmp_values"): + self.writeline("__attr __tmp_values[%d];" % self.max_accessor_index) # Add temporary variable usage details. if self.uses_temp(name, "__tmp_private_context"): self.writeline("__attr __tmp_private_context;") - if self.uses_temp(name, "__tmp_value"): - self.writeline("__attr __tmp_value;") if self.uses_temp(name, "__tmp_target_value"): self.writeline("__attr __tmp_target_value;") if self.uses_temp(name, "__tmp_result"): diff -r 51ba0ae74af2 -r 8f8361de472a transresults.py --- a/transresults.py Mon Jul 16 23:00:47 2018 +0200 +++ b/transresults.py Sat Jul 21 23:19:26 2018 +0200 @@ -3,7 +3,7 @@ """ Translation result abstractions. -Copyright (C) 2016, 2017 Paul Boddie +Copyright (C) 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 @@ -179,13 +179,15 @@ "A translation result for an attribute access." def __init__(self, instructions, refs, location, context_identity, - context_identity_verified, accessor_test): + context_identity_verified, accessor_test, accessor_stored): + 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 + self.accessor_stored = accessor_stored def references(self): return self.refs @@ -202,6 +204,9 @@ def tests_accessor(self): return self.accessor_test + def stores_accessor(self): + return self.accessor_stored + def get_origin(self): return self.refs and len(self.refs) == 1 and first(self.refs).get_origin() @@ -220,8 +225,10 @@ return encode_instructions(self.instructions) def __repr__(self): - return "AttrResult(%r, %r, %r, %r, %r)" % (self.instructions, self.refs, - self.location, self.context_identity, self.accessor_test) + return "AttrResult(%r, %r, %r, %r, %r, %r, %r)" % ( + self.instructions, self.refs, self.location, + self.context_identity, self.context_identity_verified, + self.accessor_test, self.accessor_stored) class AliasResult(NameRef, Result):