1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/micropython/code.py Sat Jul 02 02:41:33 2011 +0200
1.3 @@ -0,0 +1,298 @@
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 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.common import *
1.27 +from micropython.data 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, optimisations):
1.36 +
1.37 + "Initialise the assembler with an optimiser and status attributes."
1.38 +
1.39 + # Optimisation.
1.40 +
1.41 + self.optimiser = Optimiser(self, optimisations)
1.42 +
1.43 + # The current unit being translated.
1.44 +
1.45 + self.unit = None
1.46 +
1.47 + # The temporary storage used by the current assignment expression.
1.48 +
1.49 + self.expr_temp = []
1.50 +
1.51 + # Wiring within the code.
1.52 +
1.53 + self.labels = {}
1.54 + self.label_number = 0
1.55 + self.loop_blocks = []
1.56 + self.exception_blocks = []
1.57 +
1.58 + def reset(self):
1.59 +
1.60 + "Reset the state of the assembler."
1.61 +
1.62 + # The code itself. This is limited to the code for a particular block
1.63 + # being processed.
1.64 +
1.65 + self.blocks = []
1.66 +
1.67 + # Information about temporary values.
1.68 +
1.69 + self.temp_positions = set()
1.70 + self.max_temp_position = -1
1.71 +
1.72 + # Information about instructions which construct frames.
1.73 +
1.74 + self.frame_makers = []
1.75 +
1.76 + # Optimiser state must be reset for each unit.
1.77 +
1.78 + self.optimiser.reset()
1.79 +
1.80 + def new_block(self):
1.81 +
1.82 + "Return a new code block."
1.83 +
1.84 + return Block()
1.85 +
1.86 + def set_block(self, block):
1.87 +
1.88 + "Add the given 'block' to the unit's list of blocks."
1.89 +
1.90 + self.optimiser.reset()
1.91 + self.blocks.append(block)
1.92 +
1.93 + def get_loop_blocks(self):
1.94 + return self.loop_blocks[-1]
1.95 +
1.96 + def add_loop_blocks(self, next_block, exit_block):
1.97 + self.loop_blocks.append((next_block, exit_block))
1.98 +
1.99 + def drop_loop_blocks(self):
1.100 + self.loop_blocks.pop()
1.101 +
1.102 + def add_exception_unit(self):
1.103 + self.exception_blocks.append([])
1.104 +
1.105 + def get_exception_blocks(self):
1.106 + return self.exception_blocks[-1][-1]
1.107 +
1.108 + def add_exception_blocks(self, handler_block, exit_block):
1.109 + self.exception_blocks[-1].append((handler_block, exit_block))
1.110 +
1.111 + def drop_exception_blocks(self):
1.112 + self.exception_blocks[-1].pop()
1.113 +
1.114 + def drop_exception_unit(self):
1.115 + self.exception_blocks.pop()
1.116 +
1.117 + # Assignment expression values.
1.118 +
1.119 + def record_value(self, immediate=1):
1.120 +
1.121 + """
1.122 + Record the current active value for an assignment. If the optional
1.123 + 'immediate' parameter if set to a false value always allocates new
1.124 + temporary storage to hold the recorded value; otherwise, the
1.125 + value-providing instruction may be replicated in order to provide the
1.126 + active value later on.
1.127 + """
1.128 +
1.129 + if immediate:
1.130 + temp = self.optimiser.optimise_temp_storage()
1.131 + else:
1.132 + temp = self.get_temp()
1.133 + self.expr_temp.append(temp)
1.134 +
1.135 + def discard_value(self):
1.136 +
1.137 + "Discard any temporary storage in use for the current assignment value."
1.138 +
1.139 + self.discard_temp(self.expr_temp.pop())
1.140 +
1.141 + def set_source(self):
1.142 +
1.143 + """
1.144 + Set the source of an assignment using the current assignment value. This
1.145 + sets the source input for the current instruction.
1.146 + """
1.147 +
1.148 + self.optimiser.set_source(self.expr_temp[-1])
1.149 +
1.150 + # Optimise away constant storage if appropriate.
1.151 +
1.152 + if self.optimiser.optimise_constant_storage():
1.153 + self.remove_op()
1.154 +
1.155 + def is_immediate_user(self, node):
1.156 +
1.157 + """
1.158 + Return whether 'node' is an immediate user of an assignment expression.
1.159 + """
1.160 +
1.161 + return isinstance(node, (compiler.ast.AssName, compiler.ast.AssAttr))
1.162 +
1.163 + def has_immediate_usage(self, nodes):
1.164 +
1.165 + """
1.166 + Return whether 'nodes' are all immediate users of an assignment expression.
1.167 + """
1.168 +
1.169 + for n in nodes:
1.170 + if not self.is_immediate_user(n):
1.171 + return 0
1.172 + return 1
1.173 +
1.174 + # Temporary storage administration.
1.175 +
1.176 + def get_temp(self):
1.177 +
1.178 + """
1.179 + Add a temporary storage instruction for the current value and return a
1.180 + sequence of access instructions.
1.181 + """
1.182 +
1.183 + position_in_frame = self.reserve_temp()
1.184 + self.new_op(StoreTemp(position_in_frame))
1.185 + return LoadTemp(position_in_frame)
1.186 +
1.187 + def reserve_temp(self, temp_position=None):
1.188 +
1.189 + """
1.190 + Reserve a new temporary storage position, or if the optional
1.191 + 'temp_position' is specified, ensure that this particular position is
1.192 + reserved.
1.193 + """
1.194 +
1.195 + if temp_position is not None:
1.196 + pass
1.197 + elif not self.temp_positions:
1.198 + temp_position = 0
1.199 + else:
1.200 + temp_position = max(self.temp_positions) + 1
1.201 +
1.202 + self.temp_positions.add(temp_position)
1.203 + self.max_temp_position = max(self.max_temp_position, temp_position)
1.204 + return self.unit.all_local_usage + temp_position # position in frame
1.205 +
1.206 + def ensure_temp(self, instruction=None):
1.207 +
1.208 + """
1.209 + Ensure that the 'instruction' is using a reserved temporary storage
1.210 + position.
1.211 + """
1.212 +
1.213 + if isinstance(instruction, LoadTemp):
1.214 + temp_position = instruction.attr - self.unit.all_local_usage
1.215 + self.reserve_temp(temp_position)
1.216 +
1.217 + def discard_temp(self, instruction=None):
1.218 +
1.219 + "Discard any temporary storage position used by 'instruction'."
1.220 +
1.221 + if isinstance(instruction, LoadTemp):
1.222 + temp_position = instruction.attr - self.unit.all_local_usage
1.223 + self.free_temp(temp_position)
1.224 +
1.225 + def free_temp(self, temp_position):
1.226 +
1.227 + "Free the temporary storage position specified by 'temp_position'."
1.228 +
1.229 + if temp_position in self.temp_positions:
1.230 + self.temp_positions.remove(temp_position)
1.231 +
1.232 + def set_frame_usage(self, node, extend):
1.233 +
1.234 + """
1.235 + Ensure that the frame usage for the unit associated with 'node' is set
1.236 + on the 'extend' instruction.
1.237 + """
1.238 +
1.239 + # Remove any ExtendFrame instructions which do nothing.
1.240 +
1.241 + if self.last_op() is extend:
1.242 + self.remove_op()
1.243 + return
1.244 +
1.245 + ntemp = self.max_temp_position + 1
1.246 + extend.attr = ntemp + node.unit.local_usage # NOTE: See get_code for similar code.
1.247 +
1.248 + # Code writing methods.
1.249 +
1.250 + def new_op(self, op):
1.251 +
1.252 + """
1.253 + Add 'op' to the generated code, returning a true value if an instruction
1.254 + was added.
1.255 + """
1.256 +
1.257 + # Optimise load operations employed by this instruction.
1.258 +
1.259 + self.optimiser.optimise_load_operations(op)
1.260 + if self.optimiser.optimise_away_no_operations(op) or self.optimiser.optimise_unused_handlers(op):
1.261 + return 0
1.262 +
1.263 + # Add the operation to the current block.
1.264 +
1.265 + self.blocks[-1].code.append(op)
1.266 + self.optimiser.set_new(op)
1.267 + return 1
1.268 +
1.269 + def remove_op(self):
1.270 +
1.271 + "Remove the last instruction."
1.272 +
1.273 + op = self.blocks[-1].code.pop()
1.274 + self.optimiser.clear_active()
1.275 +
1.276 + def replace_op(self, op):
1.277 +
1.278 + "Replace the last added instruction with 'op'."
1.279 +
1.280 + self.remove_op()
1.281 + self.new_op(op)
1.282 +
1.283 + def replace_active_value(self, op):
1.284 +
1.285 + """
1.286 + Replace the value-providing active instruction with 'op' if appropriate.
1.287 + """
1.288 +
1.289 + self.optimiser.remove_active_value()
1.290 + self.new_op(op)
1.291 +
1.292 + def last_op(self):
1.293 +
1.294 + "Return the last added instruction."
1.295 +
1.296 + try:
1.297 + return self.blocks[-1].code[-1]
1.298 + except IndexError:
1.299 + return None
1.300 +
1.301 +# vim: tabstop=4 expandtab shiftwidth=4