1.1 --- a/encoders.py Mon Jul 16 23:00:47 2018 +0200
1.2 +++ b/encoders.py Sat Jul 21 23:19:26 2018 +0200
1.3 @@ -259,6 +259,14 @@
1.4 "__load_static_ignore", "__load_static_replace", "__load_static_test", "<test_context_static>",
1.5 )
1.6
1.7 +accessor_values = (
1.8 + "<accessor>",
1.9 + )
1.10 +
1.11 +accessor_ops = (
1.12 + "<accessor>", "<set_accessor>",
1.13 + )
1.14 +
1.15 context_values = (
1.16 "<context>",
1.17 )
1.18 @@ -278,7 +286,7 @@
1.19 "<accessor>", "<context>", "<name>", "<private_context>", "<target_accessor>"
1.20 )
1.21
1.22 -def encode_access_instruction(instruction, subs, context_index):
1.23 +def encode_access_instruction(instruction, subs, accessor_index, context_index):
1.24
1.25 """
1.26 Encode the 'instruction' - a sequence starting with an operation and
1.27 @@ -288,6 +296,9 @@
1.28 The 'subs' parameter defines a mapping of substitutions for special values
1.29 used in instructions.
1.30
1.31 + The 'accessor_index' parameter defines the position in local accessor
1.32 + storage for the referenced accessor or affected by an accessor operation.
1.33 +
1.34 The 'context_index' parameter defines the position in local context storage
1.35 for the referenced context or affected by a context operation.
1.36
1.37 @@ -304,7 +315,7 @@
1.38 if args:
1.39 converting_op = op
1.40 for arg in args:
1.41 - s, _substituted = encode_access_instruction_arg(arg, subs, converting_op, context_index)
1.42 + s, _substituted = encode_access_instruction_arg(arg, subs, converting_op, accessor_index, context_index)
1.43 substituted.update(_substituted)
1.44 a.append(s)
1.45 converting_op = None
1.46 @@ -326,6 +337,11 @@
1.47 elif op in static_ops:
1.48 a[-1] = "&%s" % a[-1]
1.49
1.50 + # Add accessor storage information to certain operations.
1.51 +
1.52 + if op in accessor_ops:
1.53 + a.insert(0, accessor_index)
1.54 +
1.55 # Add context storage information to certain operations.
1.56
1.57 if op in context_ops:
1.58 @@ -365,19 +381,19 @@
1.59
1.60 return "%s%s" % (op, argstr), substituted
1.61
1.62 -def encode_access_instruction_arg(arg, subs, op, context_index):
1.63 +def encode_access_instruction_arg(arg, subs, op, accessor_index, context_index):
1.64
1.65 """
1.66 Encode 'arg' using 'subs' to define substitutions, 'op' to indicate the
1.67 - operation to which the argument belongs, and 'context_index' to indicate any
1.68 - affected context storage.
1.69 + operation to which the argument belongs, and with 'accessor_index' and
1.70 + 'context_index' indicating any affected accessor and context storage.
1.71
1.72 Return a tuple containing the encoded form of 'arg' along with a collection
1.73 of any substituted values.
1.74 """
1.75
1.76 if isinstance(arg, tuple):
1.77 - encoded, substituted = encode_access_instruction(arg, subs, context_index)
1.78 + encoded, substituted = encode_access_instruction(arg, subs, accessor_index, context_index)
1.79 return attribute_to_reference(op, arg[0], encoded, substituted)
1.80
1.81 # Special values only need replacing, not encoding.
1.82 @@ -386,7 +402,7 @@
1.83
1.84 # Handle values modified by storage details.
1.85
1.86 - if arg in context_values:
1.87 + if arg in accessor_values or arg in context_values:
1.88 encoded = "%s(%s)" % (subs.get(arg), context_index)
1.89 else:
1.90 encoded = subs.get(arg)
2.1 --- a/templates/ops.h Mon Jul 16 23:00:47 2018 +0200
2.2 +++ b/templates/ops.h Sat Jul 21 23:19:26 2018 +0200
2.3 @@ -107,10 +107,11 @@
2.4 __attr __test_context_revert(int target, __attr context, __attr attr, __attr contexts[]);
2.5 __attr __test_context_static(int target, __attr context, __ref value, __attr contexts[]);
2.6
2.7 +#define __get_accessor(__TARGET) (__tmp_values[__TARGET])
2.8 #define __get_context(__TARGET) (__tmp_contexts[__TARGET])
2.9 #define __set_context(__TARGET, __ATTR) (__tmp_contexts[__TARGET] = (__ATTR))
2.10 #define __set_private_context(__ATTR) (__tmp_private_context = (__ATTR))
2.11 -#define __set_accessor(__ATTR) (__tmp_value = (__ATTR))
2.12 +#define __set_accessor(__TARGET, __ATTR) (__tmp_values[__TARGET] = (__ATTR))
2.13 #define __set_target_accessor(__ATTR) (__tmp_target_value = (__ATTR))
2.14
2.15 /* Context testing for invocations. */
3.1 --- a/tests/nested_calls.py Mon Jul 16 23:00:47 2018 +0200
3.2 +++ b/tests/nested_calls.py Sat Jul 21 23:19:26 2018 +0200
3.3 @@ -13,3 +13,31 @@
3.4
3.5 c = C(3)
3.6 print c.length() # 6
3.7 +
3.8 +# Explicit function for addition purposes.
3.9 +
3.10 +def combine(x, y, z):
3.11 + return x + y + z
3.12 +
3.13 +class Tree:
3.14 + def __init__(self, item, left=None, right=None):
3.15 + self.item = item
3.16 + self.left = left
3.17 + self.right = right
3.18 +
3.19 + def count(self):
3.20 + if self.left and self.right:
3.21 + # Test calls in parameter lists needing separate temporary storage.
3.22 + return combine(self.item, self.left.count(), self.right.count())
3.23 + else:
3.24 + return self.item
3.25 +
3.26 +tree = \
3.27 + Tree(10000,
3.28 + Tree(2000,
3.29 + Tree(300),
3.30 + Tree(40)
3.31 + ),
3.32 + Tree(5))
3.33 +
3.34 +print tree.count() # 12345
4.1 --- a/translator.py Mon Jul 16 23:00:47 2018 +0200
4.2 +++ b/translator.py Sat Jul 21 23:19:26 2018 +0200
4.3 @@ -115,6 +115,7 @@
4.4
4.5 self.namespaces = []
4.6 self.in_conditional = False
4.7 + self.in_parameter_list = False
4.8
4.9 # Exception raising adjustments.
4.10
4.11 @@ -317,6 +318,8 @@
4.12 self.max_function_target = 0
4.13 self.context_index = 0
4.14 self.max_context_index = 0
4.15 + self.accessor_index = 0
4.16 + self.max_accessor_index = 0
4.17 self.start_module()
4.18 self.process_structure(node)
4.19 self.end_module()
4.20 @@ -554,11 +557,13 @@
4.21 # The context set or retrieved will be that used by any enclosing
4.22 # invocation.
4.23
4.24 + accessor_index = self.accessor_index
4.25 context_index = self.context_index
4.26 context_identity = None
4.27 context_identity_verified = False
4.28 final_identity = None
4.29 accessor_test = False
4.30 + accessor_stored = False
4.31
4.32 # Obtain encoded versions of each instruction, accumulating temporary
4.33 # variables.
4.34 @@ -568,7 +573,9 @@
4.35 # Intercept a special instruction identifying the context.
4.36
4.37 if instruction[0] in ("<context_identity>", "<context_identity_verified>"):
4.38 - context_identity, _substituted = encode_access_instruction_arg(instruction[1], subs, instruction[0], context_index)
4.39 + context_identity, _substituted = \
4.40 + encode_access_instruction_arg(instruction[1], subs, instruction[0],
4.41 + accessor_index, context_index)
4.42 context_identity_verified = instruction[0] == "<context_identity_verified>"
4.43 continue
4.44
4.45 @@ -585,10 +592,16 @@
4.46 instruction = ("__to_error", instruction)
4.47 accessor_test = True
4.48
4.49 + # Intercept accessor storage.
4.50 +
4.51 + elif instruction[0] == "<set_accessor>":
4.52 + accessor_stored = True
4.53 +
4.54 # Collect the encoded instruction, noting any temporary variables
4.55 # required by it.
4.56
4.57 - encoded, _substituted = encode_access_instruction(instruction, subs, context_index)
4.58 + encoded, _substituted = encode_access_instruction(instruction, subs,
4.59 + accessor_index, context_index)
4.60 output.append(encoded)
4.61 substituted.update(_substituted)
4.62
4.63 @@ -604,7 +617,9 @@
4.64 refs = set([self.importer.identify(final_identity)])
4.65
4.66 del self.attrs[0]
4.67 - return AttrResult(output, refs, location, context_identity, context_identity_verified, accessor_test)
4.68 + return AttrResult(output, refs, location,
4.69 + context_identity, context_identity_verified,
4.70 + accessor_test, accessor_stored)
4.71
4.72 def init_substitutions(self):
4.73
4.74 @@ -619,21 +634,22 @@
4.75 # Substitutions used by instructions.
4.76
4.77 "<private_context>" : "__tmp_private_context",
4.78 - "<accessor>" : "__tmp_value",
4.79 "<target_accessor>" : "__tmp_target_value",
4.80
4.81 # Mappings to be replaced by those given below.
4.82
4.83 + "<accessor>" : "__tmp_values",
4.84 "<context>" : "__tmp_contexts",
4.85 "<test_context_revert>" : "__tmp_contexts",
4.86 "<test_context_static>" : "__tmp_contexts",
4.87 "<set_context>" : "__tmp_contexts",
4.88 "<set_private_context>" : "__tmp_private_context",
4.89 - "<set_accessor>" : "__tmp_value",
4.90 + "<set_accessor>" : "__tmp_values",
4.91 "<set_target_accessor>" : "__tmp_target_value",
4.92 }
4.93
4.94 self.op_subs = {
4.95 + "<accessor>" : "__get_accessor",
4.96 "<context>" : "__get_context",
4.97 "<test_context_revert>" : "__test_context_revert",
4.98 "<test_context_static>" : "__test_context_static",
4.99 @@ -840,6 +856,8 @@
4.100 self.max_function_target = 0
4.101 self.context_index = 0
4.102 self.max_context_index = 0
4.103 + self.accessor_index = 0
4.104 + self.max_accessor_index = 0
4.105
4.106 # Volatile locals for exception handling.
4.107
4.108 @@ -1082,6 +1100,7 @@
4.109 # With such operations present, the expression cannot be eliminated.
4.110
4.111 tests_accessor = have_access_context and expr.tests_accessor()
4.112 + stores_accessor = have_access_context and expr.stores_accessor()
4.113
4.114 # Parameter details and parameter list dimensions.
4.115
4.116 @@ -1258,6 +1277,9 @@
4.117 if need_context_stored:
4.118 self.record_temp("__tmp_contexts")
4.119
4.120 + if stores_accessor:
4.121 + self.record_temp("__tmp_values")
4.122 +
4.123 # Arguments are presented in a temporary frame array with any context
4.124 # always being the first argument. Where it would be unused, it may be
4.125 # set to null.
4.126 @@ -1284,6 +1306,7 @@
4.127
4.128 function_target = self.function_target
4.129 context_index = self.context_index
4.130 + accessor_index = self.accessor_index
4.131
4.132 if need_target_stored:
4.133 self.next_target()
4.134 @@ -1291,6 +1314,12 @@
4.135 if need_context_stored:
4.136 self.next_context()
4.137
4.138 + if stores_accessor:
4.139 + self.next_accessor()
4.140 +
4.141 + in_parameter_list = self.in_parameter_list
4.142 + self.in_parameter_list = True
4.143 +
4.144 for i, arg in enumerate(n.args):
4.145 argexpr = self.process_structure_node(arg)
4.146
4.147 @@ -1329,8 +1358,12 @@
4.148
4.149 # Reference the current target again.
4.150
4.151 - self.function_target = function_target
4.152 - self.context_index = context_index
4.153 + self.in_parameter_list = in_parameter_list
4.154 +
4.155 + if not self.in_parameter_list:
4.156 + self.function_target = function_target
4.157 + self.context_index = context_index
4.158 + self.accessor_index = accessor_index
4.159
4.160 # Defaults are added to the frame where arguments are missing.
4.161
4.162 @@ -1473,6 +1506,13 @@
4.163 self.context_index += 1
4.164 self.max_context_index = max(self.context_index, self.max_context_index)
4.165
4.166 + def next_accessor(self):
4.167 +
4.168 + "Allocate the next accessor value storage."
4.169 +
4.170 + self.accessor_index += 1
4.171 + self.max_accessor_index = max(self.accessor_index, self.max_accessor_index)
4.172 +
4.173 def always_callable(self, refs):
4.174
4.175 "Determine whether all 'refs' are callable."
4.176 @@ -1527,8 +1567,9 @@
4.177
4.178 name = self.get_lambda_name()
4.179 function_name = self.get_object_path(name)
4.180 -
4.181 - defaults = self.process_function_defaults(n, name, function_name, "__tmp_value")
4.182 + instance_name = "__get_accessor(%d)" % self.accessor_index
4.183 +
4.184 + defaults = self.process_function_defaults(n, name, function_name, instance_name)
4.185
4.186 # Without defaults, produce an attribute referring to the function.
4.187
4.188 @@ -1539,11 +1580,16 @@
4.189 # copy.
4.190
4.191 else:
4.192 - self.record_temp("__tmp_value")
4.193 - return make_expression("(__tmp_value = __ATTRVALUE(__COPY(&%s, sizeof(%s))), %s, __tmp_value)" % (
4.194 + self.record_temp("__tmp_values")
4.195 + return make_expression("""\
4.196 +(__set_accessor(%d, __ATTRVALUE(__COPY(&%s, sizeof(%s)))),
4.197 + %s,
4.198 + __get_accessor(%d))""" % (
4.199 + self.accessor_index,
4.200 encode_path(function_name),
4.201 encode_symbol("obj", function_name),
4.202 - ", ".join(defaults)))
4.203 + ", ".join(defaults),
4.204 + self.accessor_index))
4.205
4.206 def process_logical_node(self, n):
4.207
4.208 @@ -2119,24 +2165,21 @@
4.209
4.210 "Write temporary storage employed by 'name'."
4.211
4.212 - # Provide space for the given number of targets.
4.213 -
4.214 - targets = self.max_function_target
4.215 + # Provide space for the recorded number of temporary variables.
4.216
4.217 if self.uses_temp(name, "__tmp_targets"):
4.218 - self.writeline("__attr __tmp_targets[%d];" % targets)
4.219 -
4.220 - index = self.max_context_index
4.221 + self.writeline("__attr __tmp_targets[%d];" % self.max_function_target)
4.222
4.223 if self.uses_temp(name, "__tmp_contexts"):
4.224 - self.writeline("__attr __tmp_contexts[%d];" % index)
4.225 + self.writeline("__attr __tmp_contexts[%d];" % self.max_context_index)
4.226 +
4.227 + if self.uses_temp(name, "__tmp_values"):
4.228 + self.writeline("__attr __tmp_values[%d];" % self.max_accessor_index)
4.229
4.230 # Add temporary variable usage details.
4.231
4.232 if self.uses_temp(name, "__tmp_private_context"):
4.233 self.writeline("__attr __tmp_private_context;")
4.234 - if self.uses_temp(name, "__tmp_value"):
4.235 - self.writeline("__attr __tmp_value;")
4.236 if self.uses_temp(name, "__tmp_target_value"):
4.237 self.writeline("__attr __tmp_target_value;")
4.238 if self.uses_temp(name, "__tmp_result"):
5.1 --- a/transresults.py Mon Jul 16 23:00:47 2018 +0200
5.2 +++ b/transresults.py Sat Jul 21 23:19:26 2018 +0200
5.3 @@ -3,7 +3,7 @@
5.4 """
5.5 Translation result abstractions.
5.6
5.7 -Copyright (C) 2016, 2017 Paul Boddie <paul@boddie.org.uk>
5.8 +Copyright (C) 2016, 2017, 2018 Paul Boddie <paul@boddie.org.uk>
5.9
5.10 This program is free software; you can redistribute it and/or modify it under
5.11 the terms of the GNU General Public License as published by the Free Software
5.12 @@ -179,13 +179,15 @@
5.13 "A translation result for an attribute access."
5.14
5.15 def __init__(self, instructions, refs, location, context_identity,
5.16 - context_identity_verified, accessor_test):
5.17 + context_identity_verified, accessor_test, accessor_stored):
5.18 +
5.19 InstructionSequence.__init__(self, instructions)
5.20 self.refs = refs
5.21 self.location = location
5.22 self.context_identity = context_identity
5.23 self.context_identity_verified = context_identity_verified
5.24 self.accessor_test = accessor_test
5.25 + self.accessor_stored = accessor_stored
5.26
5.27 def references(self):
5.28 return self.refs
5.29 @@ -202,6 +204,9 @@
5.30 def tests_accessor(self):
5.31 return self.accessor_test
5.32
5.33 + def stores_accessor(self):
5.34 + return self.accessor_stored
5.35 +
5.36 def get_origin(self):
5.37 return self.refs and len(self.refs) == 1 and first(self.refs).get_origin()
5.38
5.39 @@ -220,8 +225,10 @@
5.40 return encode_instructions(self.instructions)
5.41
5.42 def __repr__(self):
5.43 - return "AttrResult(%r, %r, %r, %r, %r)" % (self.instructions, self.refs,
5.44 - self.location, self.context_identity, self.accessor_test)
5.45 + return "AttrResult(%r, %r, %r, %r, %r, %r, %r)" % (
5.46 + self.instructions, self.refs, self.location,
5.47 + self.context_identity, self.context_identity_verified,
5.48 + self.accessor_test, self.accessor_stored)
5.49
5.50 class AliasResult(NameRef, Result):
5.51