1.1 --- a/encoders.py Fri Jan 20 17:39:52 2017 +0100
1.2 +++ b/encoders.py Fri Jan 20 18:39:33 2017 +0100
1.3 @@ -194,10 +194,13 @@
1.4
1.5 The 'subs' parameter defines a mapping of substitutions for special values
1.6 used in instructions.
1.7 +
1.8 + Return both the encoded instruction and a collection of substituted names.
1.9 """
1.10
1.11 op = instruction[0]
1.12 args = instruction[1:]
1.13 + substituted = set()
1.14
1.15 if not args:
1.16 argstr = ""
1.17 @@ -208,7 +211,9 @@
1.18 a = []
1.19 converting_op = op
1.20 for arg in args:
1.21 - a.append(encode_access_instruction_arg(arg, subs, converting_op))
1.22 + s, _substituted = encode_access_instruction_arg(arg, subs, converting_op)
1.23 + substituted.update(_substituted)
1.24 + a.append(s)
1.25 converting_op = None
1.26
1.27 # Modify certain arguments.
1.28 @@ -250,40 +255,45 @@
1.29 # operation at all.
1.30
1.31 if subs.has_key(op):
1.32 + substituted.add(op)
1.33 op = subs[op]
1.34 elif not args:
1.35 op = "&%s" % encode_path(op)
1.36
1.37 - return "%s%s" % (op, argstr)
1.38 + return "%s%s" % (op, argstr), substituted
1.39
1.40 def encode_access_instruction_arg(arg, subs, op):
1.41
1.42 - "Encode 'arg' using 'subs' to define substitutions."
1.43 + """
1.44 + Encode 'arg' using 'subs' to define substitutions, returning a tuple
1.45 + containing the encoded form of 'arg' along with a collection of any
1.46 + substituted values.
1.47 + """
1.48
1.49 if isinstance(arg, tuple):
1.50 - encoded = encode_access_instruction(arg, subs)
1.51 + encoded, substituted = encode_access_instruction(arg, subs)
1.52
1.53 # Convert attribute results to references where required.
1.54
1.55 if op and op in reference_acting_ops and arg[0] in attribute_producing_ops:
1.56 - return "%s.value" % encoded
1.57 + return "%s.value" % encoded, substituted
1.58 else:
1.59 - return encoded
1.60 + return encoded, substituted
1.61
1.62 # Special values only need replacing, not encoding.
1.63
1.64 elif subs.has_key(arg):
1.65 - return subs.get(arg)
1.66 + return subs.get(arg), set([arg])
1.67
1.68 # Convert static references to the appropriate type.
1.69
1.70 elif op and op in reference_acting_ops and arg != "<accessor>":
1.71 - return "&%s" % encode_path(arg)
1.72 + return "&%s" % encode_path(arg), set()
1.73
1.74 # Other values may need encoding.
1.75
1.76 else:
1.77 - return encode_path(arg)
1.78 + return encode_path(arg), set()
1.79
1.80 def encode_bound_reference(path):
1.81
3.1 --- a/translator.py Fri Jan 20 17:39:52 2017 +0100
3.2 +++ b/translator.py Fri Jan 20 18:39:33 2017 +0100
3.3 @@ -3,7 +3,7 @@
3.4 """
3.5 Translate programs.
3.6
3.7 -Copyright (C) 2015, 2016 Paul Boddie <paul@boddie.org.uk>
3.8 +Copyright (C) 2015, 2016, 2017 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 @@ -29,6 +29,7 @@
3.13 from os.path import exists, join
3.14 from os import makedirs
3.15 from referencing import Reference
3.16 +from StringIO import StringIO
3.17 import compiler
3.18 import results
3.19
3.20 @@ -41,6 +42,7 @@
3.21 self.deducer = deducer
3.22 self.optimiser = optimiser
3.23 self.output = output
3.24 + self.modules = {}
3.25
3.26 def to_output(self):
3.27 output = join(self.output, "src")
3.28 @@ -55,6 +57,7 @@
3.29 if parts[0] != "native":
3.30 tm = TranslatedModule(module.name, self.importer, self.deducer, self.optimiser)
3.31 tm.translate(module.filename, join(output, "%s.c" % module.name))
3.32 + self.modules[module.name] = tm
3.33
3.34 # Classes representing intermediate translation results.
3.35
3.36 @@ -267,7 +270,7 @@
3.37
3.38 # Output stream.
3.39
3.40 - self.out = None
3.41 + self.out_toplevel = self.out = None
3.42 self.indent = 0
3.43 self.tabstop = " "
3.44
3.45 @@ -286,6 +289,10 @@
3.46 self.attr_accesses = {}
3.47 self.attr_accessors = {}
3.48
3.49 + # Special variable usage.
3.50 +
3.51 + self.temp_usage = {}
3.52 +
3.53 def __repr__(self):
3.54 return "TranslatedModule(%r, %r)" % (self.name, self.importer)
3.55
3.56 @@ -307,7 +314,7 @@
3.57
3.58 self.reset_lambdas()
3.59
3.60 - self.out = open(output_filename, "w")
3.61 + self.out_toplevel = self.out = open(output_filename, "w")
3.62 try:
3.63 self.start_output()
3.64
3.65 @@ -696,15 +703,42 @@
3.66 subs = {
3.67 "<expr>" : str(attr_expr),
3.68 "<assexpr>" : str(self.in_assignment),
3.69 + }
3.70 +
3.71 + temp_subs = {
3.72 "<context>" : "__tmp_context",
3.73 "<accessor>" : "__tmp_value",
3.74 "<target_accessor>" : "__tmp_target_value",
3.75 + "<set_accessor>" : "__tmp_value",
3.76 + "<set_target_accessor>" : "__tmp_target_value",
3.77 }
3.78
3.79 + op_subs = {
3.80 + "<set_accessor>" : "__set_accessor",
3.81 + "<set_target_accessor>" : "__set_target_accessor",
3.82 + }
3.83 +
3.84 + subs.update(temp_subs)
3.85 + subs.update(op_subs)
3.86 +
3.87 output = []
3.88 + substituted = set()
3.89 +
3.90 + # Obtain encoded versions of each instruction, accumulating temporary
3.91 + # variables.
3.92
3.93 for instruction in self.optimiser.access_instructions[location]:
3.94 - output.append(encode_access_instruction(instruction, subs))
3.95 + encoded, _substituted = encode_access_instruction(instruction, subs)
3.96 + output.append(encoded)
3.97 + substituted.update(_substituted)
3.98 +
3.99 + # Record temporary name usage.
3.100 +
3.101 + for sub in substituted:
3.102 + if temp_subs.has_key(sub):
3.103 + self.record_temp(temp_subs[sub])
3.104 +
3.105 + # Format the output.
3.106
3.107 if len(output) == 1:
3.108 out = output[0]
3.109 @@ -1146,6 +1180,7 @@
3.110 # set to null.
3.111
3.112 if context_required:
3.113 + self.record_temp("__tmp_targets")
3.114 args = ["__CONTEXT_AS_VALUE(__tmp_targets[%d])" % self.function_target]
3.115 else:
3.116 args = ["__NULL"]
3.117 @@ -1240,6 +1275,7 @@
3.118 # Without a known specific callable, the expression provides the target.
3.119
3.120 if not target or context_required:
3.121 + self.record_temp("__tmp_targets")
3.122 stages.append("__tmp_targets[%d] = %s" % (self.function_target, expr))
3.123
3.124 # Any specific callable is then obtained.
3.125 @@ -1247,6 +1283,7 @@
3.126 if target:
3.127 stages.append(target)
3.128 elif function:
3.129 + self.record_temp("__tmp_targets")
3.130 stages.append("__load_via_object(__tmp_targets[%d].value, %s).fn" % (
3.131 self.function_target, encode_symbol("pos", "__fn__")))
3.132
3.133 @@ -1259,6 +1296,7 @@
3.134 # the callable and argument collections.
3.135
3.136 else:
3.137 + self.record_temp("__tmp_targets")
3.138 output = "(%s, __invoke(\n__tmp_targets[%d],\n%d, %d, %s, %s,\n%d, %s\n))" % (
3.139 ",\n".join(stages),
3.140 self.function_target,
3.141 @@ -1312,6 +1350,7 @@
3.142 # copy.
3.143
3.144 else:
3.145 + self.record_temp("__tmp_value")
3.146 return make_expression("(__tmp_value = __COPY(&%s, sizeof(%s)), %s, (__attr) {.context=0, .value=__tmp_value})" % (
3.147 encode_path(function_name),
3.148 encode_symbol("obj", function_name),
3.149 @@ -1331,6 +1370,8 @@
3.150 (__tmp_result = <a>, __BOOL(__tmp_result)) ? __tmp_result : <b>
3.151 """
3.152
3.153 + self.record_temp("__tmp_result")
3.154 +
3.155 if isinstance(n, compiler.ast.And):
3.156 op = "!"
3.157 else:
3.158 @@ -1638,6 +1679,35 @@
3.159 self.indent -= 1
3.160 self.writeline("}")
3.161
3.162 + # Special variable usage.
3.163 +
3.164 + def record_temp(self, name):
3.165 +
3.166 + """
3.167 + Record the use of the temporary 'name' in the current namespace. At the
3.168 + class or module level, the temporary name is associated with the module,
3.169 + since the variable will then be allocated in the module's own main
3.170 + program.
3.171 + """
3.172 +
3.173 + if self.in_function:
3.174 + path = self.get_namespace_path()
3.175 + else:
3.176 + path = self.name
3.177 +
3.178 + init_item(self.temp_usage, path, set)
3.179 + self.temp_usage[path].add(name)
3.180 +
3.181 + def uses_temp(self, path, name):
3.182 +
3.183 + """
3.184 + Return whether the given namespace 'path' employs a temporary variable
3.185 + with the given 'name'. Note that 'path' should only be a module or a
3.186 + function or method, not a class.
3.187 + """
3.188 +
3.189 + return self.temp_usage.has_key(path) and name in self.temp_usage[path]
3.190 +
3.191 # Output generation.
3.192
3.193 def start_output(self):
3.194 @@ -1654,6 +1724,28 @@
3.195 #include "main.h"
3.196 """
3.197
3.198 + def start_unit(self):
3.199 +
3.200 + "Record output within a generated function for later use."
3.201 +
3.202 + self.out = StringIO()
3.203 +
3.204 + def end_unit(self, name):
3.205 +
3.206 + "Add declarations and generated code."
3.207 +
3.208 + # Restore the output stream.
3.209 +
3.210 + out = self.out
3.211 + self.out = self.out_toplevel
3.212 +
3.213 + self.write_temporaries(name)
3.214 + out.seek(0)
3.215 + self.out.write(out.read())
3.216 +
3.217 + self.indent -= 1
3.218 + print >>self.out, "}"
3.219 +
3.220 def start_module(self):
3.221
3.222 "Write the start of each module's main function."
3.223 @@ -1661,14 +1753,13 @@
3.224 print >>self.out, "void __main_%s()" % encode_path(self.name)
3.225 print >>self.out, "{"
3.226 self.indent += 1
3.227 - self.write_temporaries(self.importer.function_targets.get(self.name))
3.228 + self.start_unit()
3.229
3.230 def end_module(self):
3.231
3.232 "End each module by closing its main function."
3.233
3.234 - self.indent -= 1
3.235 - print >>self.out, "}"
3.236 + self.end_unit(self.name)
3.237
3.238 def start_function(self, name):
3.239
3.240 @@ -1677,7 +1768,6 @@
3.241 print >>self.out, "__attr %s(__attr __args[])" % encode_function_pointer(name)
3.242 print >>self.out, "{"
3.243 self.indent += 1
3.244 - self.write_temporaries(self.importer.function_targets.get(name))
3.245
3.246 # Obtain local names from parameters.
3.247
3.248 @@ -1702,29 +1792,39 @@
3.249 self.writeline("__attr %s;" % ", ".join(names))
3.250
3.251 self.write_parameters(name)
3.252 + self.start_unit()
3.253
3.254 def end_function(self, name):
3.255
3.256 "End the function having the given 'name'."
3.257
3.258 - self.indent -= 1
3.259 - print >>self.out, "}"
3.260 + self.end_unit(name)
3.261 print >>self.out
3.262
3.263 - def write_temporaries(self, targets):
3.264 -
3.265 - """
3.266 - Write temporary storage employed by functions, providing space for the
3.267 - given number of 'targets'.
3.268 - """
3.269 -
3.270 - targets = targets is not None and "__tmp_targets[%d], " % targets or ""
3.271 -
3.272 - self.writeline("__ref __tmp_context, __tmp_value, __tmp_target_value;")
3.273 - self.writeline("__attr %s__tmp_result;" % targets)
3.274 + def write_temporaries(self, name):
3.275 +
3.276 + "Write temporary storage employed by 'name'."
3.277 +
3.278 + # Provide space for the given number of targets.
3.279 +
3.280 + if self.uses_temp(name, "__tmp_targets"):
3.281 + targets = self.importer.function_targets.get(name)
3.282 + self.writeline("__attr __tmp_targets[%d];" % targets)
3.283 +
3.284 + # Add temporary variable usage details.
3.285 +
3.286 + if self.uses_temp(name, "__tmp_context"):
3.287 + self.writeline("__ref __tmp_context;")
3.288 + if self.uses_temp(name, "__tmp_value"):
3.289 + self.writeline("__ref __tmp_value;")
3.290 + if self.uses_temp(name, "__tmp_target_value"):
3.291 + self.writeline("__ref __tmp_target_value;")
3.292 + if self.uses_temp(name, "__tmp_result"):
3.293 + self.writeline("__attr __tmp_result;")
3.294
3.295 module = self.importer.get_module(self.name)
3.296 - if self.get_namespace_path() in module.exception_namespaces:
3.297 +
3.298 + if name in module.exception_namespaces:
3.299 self.writeline("__exc __tmp_exc;")
3.300
3.301 def write_parameters(self, name):