1.1 --- a/micropython/code.py Fri Jun 28 21:17:02 2013 +0200
1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1.3 @@ -1,492 +0,0 @@
1.4 -#!/usr/bin/env python
1.5 -
1.6 -"""
1.7 -Generate low-level code.
1.8 -
1.9 -Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Paul Boddie <paul@boddie.org.uk>
1.10 -
1.11 -This program is free software; you can redistribute it and/or modify it under
1.12 -the terms of the GNU General Public License as published by the Free Software
1.13 -Foundation; either version 3 of the License, or (at your option) any later
1.14 -version.
1.15 -
1.16 -This program is distributed in the hope that it will be useful, but WITHOUT
1.17 -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.18 -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.19 -details.
1.20 -
1.21 -You should have received a copy of the GNU General Public License along with
1.22 -this program. If not, see <http://www.gnu.org/licenses/>.
1.23 -"""
1.24 -
1.25 -from micropython.opt import Optimiser
1.26 -from micropython.data import *
1.27 -from micropython.errors import *
1.28 -from micropython.rsvp import *
1.29 -import compiler.ast
1.30 -
1.31 -class Assembler:
1.32 -
1.33 - "Support for generating low-level code."
1.34 -
1.35 - def __init__(self, program):
1.36 -
1.37 - """
1.38 - Initialise the assembler with a program, optimiser and status
1.39 - attributes.
1.40 - """
1.41 -
1.42 - self.program = program
1.43 -
1.44 - # Optimisation.
1.45 -
1.46 - self.optimiser = Optimiser(self, program.optimisations)
1.47 -
1.48 - # The current unit being translated.
1.49 -
1.50 - self.unit = None
1.51 -
1.52 - # The temporary storage used by the current assignment expression.
1.53 -
1.54 - self.expr_temp = []
1.55 -
1.56 - # The start of the current assignment target instructions.
1.57 -
1.58 - self.target_start = None
1.59 -
1.60 - # Wiring within the code.
1.61 -
1.62 - self.labels = {}
1.63 - self.label_number = 0
1.64 - self.loop_blocks = []
1.65 - self.exception_blocks = []
1.66 -
1.67 - def reset(self):
1.68 -
1.69 - "Reset the state of the assembler."
1.70 -
1.71 - # The code itself. This is limited to the code for a particular block
1.72 - # being processed.
1.73 -
1.74 - self.blocks = []
1.75 -
1.76 - # Information about temporary values.
1.77 -
1.78 - self.temp_positions = set()
1.79 - self.max_temp_position = -1
1.80 -
1.81 - # Information about instructions which construct frames.
1.82 -
1.83 - self.frame_makers = []
1.84 -
1.85 - # Optimiser state must be reset for each unit.
1.86 -
1.87 - self.optimiser.reset()
1.88 -
1.89 - def new_block(self):
1.90 -
1.91 - "Return a new code block."
1.92 -
1.93 - return Block(self.unit)
1.94 -
1.95 - def get_block(self):
1.96 -
1.97 - "Return the current block."
1.98 -
1.99 - return self.blocks[-1]
1.100 -
1.101 - def set_block(self, block, preceding=None):
1.102 -
1.103 - """
1.104 - Add the given 'block' to the unit's list of blocks, noting any active
1.105 - value information from 'preceding' blocks on the new block.
1.106 - """
1.107 -
1.108 - self.optimiser.reset(block, preceding)
1.109 - self.blocks.append(block)
1.110 -
1.111 - def get_loop_blocks(self):
1.112 - return self.loop_blocks[-1]
1.113 -
1.114 - def add_loop_blocks(self, next_block, exit_block):
1.115 - self.loop_blocks.append((next_block, exit_block))
1.116 -
1.117 - def drop_loop_blocks(self):
1.118 - self.loop_blocks.pop()
1.119 -
1.120 - def add_exception_unit(self):
1.121 - self.exception_blocks.append([])
1.122 -
1.123 - def get_exception_blocks(self):
1.124 - return self.exception_blocks[-1][-1]
1.125 -
1.126 - def add_exception_blocks(self, handler_block, exit_block):
1.127 - self.exception_blocks[-1].append((handler_block, exit_block))
1.128 -
1.129 - def drop_exception_blocks(self):
1.130 - self.exception_blocks[-1].pop()
1.131 -
1.132 - def drop_exception_unit(self):
1.133 - self.exception_blocks.pop()
1.134 -
1.135 - # Assignment expression values.
1.136 -
1.137 - def record_value(self, immediate=1):
1.138 -
1.139 - """
1.140 - Record the current active value for an assignment. If the optional
1.141 - 'immediate' parameter is set to a false value, new temporary storage to
1.142 - hold the recorded value will be allocated; otherwise, the
1.143 - value-providing instruction may be replicated in order to provide the
1.144 - active value later on.
1.145 - """
1.146 -
1.147 - if immediate:
1.148 - temp = self.optimiser.optimise_temp_storage()
1.149 - else:
1.150 - temp = self.get_temp()
1.151 - self.expr_temp.append(temp)
1.152 -
1.153 - def discard_value(self):
1.154 -
1.155 - "Discard any temporary storage in use for the current assignment value."
1.156 -
1.157 - self.discard_temp(self.expr_temp.pop())
1.158 -
1.159 - def start_target(self):
1.160 -
1.161 - """
1.162 - Start recording instructions used to define the target of an assignment.
1.163 - """
1.164 -
1.165 - self.target_start = len(self.blocks[-1])
1.166 -
1.167 - def remove_target_ops(self):
1.168 -
1.169 - "Remove the assignment target instructions."
1.170 -
1.171 - del self.blocks[-1].code[self.target_start:]
1.172 - self.target_start = None
1.173 - self.optimiser.clear_active()
1.174 -
1.175 - def get_target_ops(self):
1.176 -
1.177 - "Return the assignment target instructions."
1.178 -
1.179 - return self.blocks[-1].code[self.target_start:]
1.180 -
1.181 - def assign_value(self, expr=None):
1.182 -
1.183 - """
1.184 - Set the source of an assignment using 'expr' or the current assignment
1.185 - value. This may set the source register of the current instruction.
1.186 - """
1.187 -
1.188 - expr = expr or self.expr_temp[-1]
1.189 -
1.190 - # Optimise away constant storage if appropriate.
1.191 -
1.192 - if self.optimiser.optimise_constant_storage(expr):
1.193 - self.remove_target_ops()
1.194 - return
1.195 -
1.196 - # Otherwise, insert the assignment source.
1.197 -
1.198 - expr_copy = expr.copy()
1.199 - assign_ops = self.get_target_ops()
1.200 -
1.201 - if not assign_ops:
1.202 - self.target_start = None
1.203 - return
1.204 -
1.205 - assign_op = assign_ops[-1]
1.206 -
1.207 - # Either insert the instruction yielding the value and adjust the
1.208 - # assignment source.
1.209 -
1.210 - expr_copy.target = "source"
1.211 -
1.212 - if self.insert_op(-1, expr_copy):
1.213 - assign_op.source = "source"
1.214 - self.update_temp(expr, expr_copy)
1.215 -
1.216 - # (Now, the instruction need not be inserted.)
1.217 -
1.218 - # Or transfer the working value to the source register.
1.219 -
1.220 - elif assign_op.working == "working":
1.221 - self.insert_op(-1, Transfer(source="working_context", target="source_context"))
1.222 - self.insert_op(-1, Transfer(source="working", target="source"))
1.223 - assign_op.source = "source"
1.224 -
1.225 - # Or let the assignment use the working register.
1.226 -
1.227 - else:
1.228 - assign_op.source = "working"
1.229 -
1.230 - self.target_start = None
1.231 -
1.232 - def set_target(self, target):
1.233 -
1.234 - "Reset the target of the active instruction to 'target'."
1.235 -
1.236 - self.optimiser.set_target("working", target)
1.237 -
1.238 - def is_immediate_user(self, node):
1.239 -
1.240 - """
1.241 - Return whether 'node' is an immediate user of an assignment expression.
1.242 - """
1.243 -
1.244 - return isinstance(node, (compiler.ast.AssName, compiler.ast.AssAttr))
1.245 -
1.246 - def has_immediate_usage(self, nodes):
1.247 -
1.248 - """
1.249 - Return whether 'nodes' are all immediate users of an assignment expression.
1.250 - """
1.251 -
1.252 - for n in nodes:
1.253 - if not self.is_immediate_user(n):
1.254 - return False
1.255 - return True
1.256 -
1.257 - # Temporary storage administration.
1.258 -
1.259 - def get_temp(self):
1.260 -
1.261 - """
1.262 - Return a temporary storage access instruction for the current value.
1.263 - Initially, this instruction is not associated with an allocated unit of
1.264 - temporary storage, and if used as a new instruction will not be added to
1.265 - the code, but if the current value changes, the 'set_temp' method will
1.266 - be called by the optimiser and a unit of storage will be allocated.
1.267 - """
1.268 -
1.269 - temp = LoadTemp(None)
1.270 - self.optimiser.request_active_value("working", temp, self.blocks[-1], len(self.blocks[-1].code))
1.271 - return temp
1.272 -
1.273 - def set_temp(self, temp, block, pos):
1.274 -
1.275 - """
1.276 - Emit a storage instruction for the given 'temp' loading instruction,
1.277 - reserving a new temporary storage location.
1.278 - """
1.279 -
1.280 - if temp.attr is None:
1.281 - temp.attr = self.reserve_temp()
1.282 - block.insert(pos, StoreTemp(temp.attr))
1.283 -
1.284 - def update_temp(self, temp, updated):
1.285 -
1.286 - "Update 'temp' using the given 'updated' instruction."
1.287 -
1.288 - if isinstance(temp, LoadTemp):
1.289 - temp.attr = updated.attr
1.290 -
1.291 - def reserve_temp(self, temp_position=None):
1.292 -
1.293 - """
1.294 - Reserve a new temporary storage position, or if the optional
1.295 - 'temp_position' is specified, ensure that this particular position is
1.296 - reserved.
1.297 - """
1.298 -
1.299 - if temp_position is not None:
1.300 - pass
1.301 - elif not self.temp_positions:
1.302 - temp_position = 0
1.303 - else:
1.304 - temp_position = max(self.temp_positions) + 1
1.305 -
1.306 - self.temp_positions.add(temp_position)
1.307 - self.max_temp_position = max(self.max_temp_position, temp_position)
1.308 - return self.unit.all_local_usage + temp_position # position in frame
1.309 -
1.310 - def ensure_temp(self, instruction=None):
1.311 -
1.312 - """
1.313 - Ensure that the 'instruction' is using a reserved temporary storage
1.314 - position.
1.315 - """
1.316 -
1.317 - if isinstance(instruction, LoadTemp):
1.318 - temp_position = instruction.attr - self.unit.all_local_usage
1.319 - self.reserve_temp(temp_position)
1.320 -
1.321 - def discard_temp(self, instruction=None):
1.322 -
1.323 - "Discard any temporary storage position used by 'instruction'."
1.324 -
1.325 - if isinstance(instruction, LoadTemp) and instruction.attr is not None:
1.326 - temp_position = instruction.attr - self.unit.all_local_usage
1.327 - self.free_temp(temp_position)
1.328 -
1.329 - # Prevent any requested active value from generating instructions now
1.330 - # that our interest in it has passed.
1.331 -
1.332 - self.optimiser.ignore_active_value("working")
1.333 -
1.334 - def free_temp(self, temp_position):
1.335 -
1.336 - "Free the temporary storage position specified by 'temp_position'."
1.337 -
1.338 - if temp_position in self.temp_positions:
1.339 - self.temp_positions.remove(temp_position)
1.340 -
1.341 - def set_frame_usage(self, node, extend):
1.342 -
1.343 - """
1.344 - Ensure that the frame usage for the unit associated with 'node' is set
1.345 - on the 'extend' instruction.
1.346 - """
1.347 -
1.348 - # Remove any ExtendFrame instructions which do nothing.
1.349 -
1.350 - if self.last_op() is extend:
1.351 - self.remove_op()
1.352 - return
1.353 -
1.354 - ntemp = self.max_temp_position + 1
1.355 - extend.attr = ntemp + node.unit.local_usage # NOTE: See get_code for similar code.
1.356 -
1.357 - # Code writing methods.
1.358 -
1.359 - def new_op(self, op):
1.360 -
1.361 - """
1.362 - Add 'op' to the generated code, returning a true value if an instruction
1.363 - was added.
1.364 - """
1.365 -
1.366 - if not self.check_op(op):
1.367 - return False
1.368 -
1.369 - # Add the operation to the current block.
1.370 -
1.371 - self.optimiser.set_new("working", op)
1.372 - self.blocks[-1].append(op)
1.373 - return True
1.374 -
1.375 - def insert_op(self, i, op):
1.376 -
1.377 - "Insert at index 'i' in the current block the instruction 'op'."
1.378 -
1.379 - if not self.check_op(op):
1.380 - return False
1.381 -
1.382 - self.blocks[-1].insert(i, op)
1.383 - return True
1.384 -
1.385 - def check_op(self, op):
1.386 -
1.387 - "Return whether the given 'op' is to be added to the code."
1.388 -
1.389 - # Optimise away temporary storage instructions where the active value is
1.390 - # still available and was not recorded.
1.391 -
1.392 - if isinstance(op, LoadTemp) and op.attr is None:
1.393 - return False
1.394 -
1.395 - # Optimise load operations employed by this instruction.
1.396 -
1.397 - if self.optimiser.optimise_away_no_operations(op) or self.optimiser.optimise_unused_handlers(op):
1.398 - return False
1.399 -
1.400 - return True
1.401 -
1.402 - def remove_op(self):
1.403 -
1.404 - "Remove the last instruction."
1.405 -
1.406 - op = self.blocks[-1].code.pop()
1.407 - self.optimiser.clear_active()
1.408 - return op
1.409 -
1.410 - def replace_op(self, op):
1.411 -
1.412 - "Replace the last added instruction with 'op'."
1.413 -
1.414 - self.remove_op()
1.415 - self.new_op(op)
1.416 -
1.417 - def replace_active_value(self, register, op):
1.418 -
1.419 - """
1.420 - Replace the active instruction providing 'register' with its value with
1.421 - the given 'op' if appropriate.
1.422 - """
1.423 -
1.424 - removed = self.optimiser.remove_active_value(register)
1.425 - self.new_op(op)
1.426 - return removed
1.427 -
1.428 - def last_op(self):
1.429 -
1.430 - "Return the last added instruction."
1.431 -
1.432 - try:
1.433 - return self.blocks[-1].code[-1]
1.434 - except IndexError:
1.435 - return None
1.436 -
1.437 - # Allocation-related methods.
1.438 -
1.439 - def make_instance(self, cls, n):
1.440 -
1.441 - """
1.442 - Request a new instance using the given class 'cls' and with 'n'
1.443 - attributes.
1.444 - """
1.445 -
1.446 - # Load the class in order to locate the instance template.
1.447 -
1.448 - self.new_op(LoadConst(cls))
1.449 -
1.450 - # NOTE: Instance headers are one location.
1.451 -
1.452 - self.new_op(MakeInstance(n + 1))
1.453 -
1.454 - def make_exception(self, name):
1.455 -
1.456 - "Make an exception of the given 'name' using 'node'."
1.457 -
1.458 - # NOTE: Reserving an attribute.
1.459 -
1.460 - self.make_instance(self.get_builtin_class(name), 1)
1.461 -
1.462 - # Name-related methods.
1.463 -
1.464 - def get_scope(self, name):
1.465 -
1.466 - "Return the scope for the given 'name'."
1.467 -
1.468 - attr, scope, from_name = self.unit._get_with_scope(name)
1.469 - return scope
1.470 -
1.471 - def load_builtin(self, name, node):
1.472 -
1.473 - "Generate an instruction loading 'name' for the given 'node'."
1.474 -
1.475 - self.new_op(LoadAddress(self.get_builtin(name)))
1.476 -
1.477 - def get_builtin_class(self, name):
1.478 -
1.479 - "Return the built-in class with the given 'name'."
1.480 -
1.481 - return self.get_builtin(name).get_value()
1.482 -
1.483 - def get_builtin(self, name):
1.484 -
1.485 - "Return the built-in module definition for the given 'name'."
1.486 -
1.487 - if self.builtins is not None:
1.488 - try:
1.489 - return self.builtins[name]
1.490 - except KeyError:
1.491 - raise TranslateError("No __builtins__ definition is available for name %r." % name)
1.492 - else:
1.493 - raise TranslateError("No __builtins__ module is available for name %r." % name)
1.494 -
1.495 -# vim: tabstop=4 expandtab shiftwidth=4