1 #!/usr/bin/env python 2 3 """ 4 Generate low-level code. 5 6 Copyright (C) 2007, 2008, 2009, 2010, 2011 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from micropython.opt import Optimiser 23 from micropython.common import * 24 from micropython.data import * 25 from micropython.rsvp import * 26 import compiler.ast 27 28 class Assembler: 29 30 "Support for generating low-level code." 31 32 def __init__(self, optimisations): 33 34 "Initialise the assembler with an optimiser and status attributes." 35 36 # Optimisation. 37 38 self.optimiser = Optimiser(self, optimisations) 39 40 # The current unit being translated. 41 42 self.unit = None 43 44 # The temporary storage used by the current assignment expression. 45 46 self.expr_temp = [] 47 48 # Wiring within the code. 49 50 self.labels = {} 51 self.label_number = 0 52 self.loop_blocks = [] 53 self.exception_blocks = [] 54 55 def reset(self): 56 57 "Reset the state of the assembler." 58 59 # The code itself. This is limited to the code for a particular block 60 # being processed. 61 62 self.blocks = [] 63 64 # Information about temporary values. 65 66 self.temp_positions = set() 67 self.max_temp_position = -1 68 69 # Information about instructions which construct frames. 70 71 self.frame_makers = [] 72 73 # Optimiser state must be reset for each unit. 74 75 self.optimiser.reset() 76 77 def new_block(self): 78 79 "Return a new code block." 80 81 return Block() 82 83 def get_block(self): 84 85 "Return the current block." 86 87 return self.blocks[-1] 88 89 def set_block(self, block, preceding=None): 90 91 """ 92 Add the given 'block' to the unit's list of blocks, noting any active 93 value information from 'preceding' blocks on the new block. 94 """ 95 96 self.optimiser.reset(block, preceding) 97 self.blocks.append(block) 98 99 def get_loop_blocks(self): 100 return self.loop_blocks[-1] 101 102 def add_loop_blocks(self, next_block, exit_block): 103 self.loop_blocks.append((next_block, exit_block)) 104 105 def drop_loop_blocks(self): 106 self.loop_blocks.pop() 107 108 def add_exception_unit(self): 109 self.exception_blocks.append([]) 110 111 def get_exception_blocks(self): 112 return self.exception_blocks[-1][-1] 113 114 def add_exception_blocks(self, handler_block, exit_block): 115 self.exception_blocks[-1].append((handler_block, exit_block)) 116 117 def drop_exception_blocks(self): 118 self.exception_blocks[-1].pop() 119 120 def drop_exception_unit(self): 121 self.exception_blocks.pop() 122 123 # Assignment expression values. 124 125 def record_value(self, immediate=1): 126 127 """ 128 Record the current active value for an assignment. If the optional 129 'immediate' parameter if set to a false value always allocates new 130 temporary storage to hold the recorded value; otherwise, the 131 value-providing instruction may be replicated in order to provide the 132 active value later on. 133 """ 134 135 if immediate: 136 temp = self.optimiser.optimise_temp_storage() 137 else: 138 temp = self.get_temp() 139 self.expr_temp.append(temp) 140 141 def discard_value(self): 142 143 "Discard any temporary storage in use for the current assignment value." 144 145 self.discard_temp(self.expr_temp.pop()) 146 147 def assign_value(self, expr=None): 148 149 """ 150 Set the source of an assignment using 'expr' or the current assignment 151 value. This sets the source register of the current instruction. 152 """ 153 154 if expr is None: 155 expr = self.expr_temp[-1] 156 157 # Optimise away constant storage if appropriate. 158 159 if self.optimiser.optimise_constant_storage(expr): 160 self.remove_op() 161 return 162 163 # Otherwise, insert the assignment source. 164 165 if expr is not None: 166 expr = expr.copy() 167 expr.target = "source" 168 self.insert_op(-1, expr) 169 self.last_op().source = "source" 170 171 def set_working(self, expr): 172 if expr is not None: 173 expr = expr.copy() 174 expr.target = "working" 175 self.insert_op(-1, expr) 176 self.last_op().source = "working" 177 178 def set_target(self, target): 179 180 "Reset the target of the active instruction to 'target'." 181 182 self.optimiser.set_target(target) 183 184 def is_immediate_user(self, node): 185 186 """ 187 Return whether 'node' is an immediate user of an assignment expression. 188 """ 189 190 return isinstance(node, (compiler.ast.AssName, compiler.ast.AssAttr)) 191 192 def has_immediate_usage(self, nodes): 193 194 """ 195 Return whether 'nodes' are all immediate users of an assignment expression. 196 """ 197 198 for n in nodes: 199 if not self.is_immediate_user(n): 200 return 0 201 return 1 202 203 # Temporary storage administration. 204 205 def get_temp(self): 206 207 """ 208 Add a temporary storage instruction for the current value and return a 209 sequence of access instructions. 210 """ 211 212 position_in_frame = self.reserve_temp() 213 self.new_op(StoreTemp(position_in_frame)) 214 return LoadTemp(position_in_frame) 215 216 def reserve_temp(self, temp_position=None): 217 218 """ 219 Reserve a new temporary storage position, or if the optional 220 'temp_position' is specified, ensure that this particular position is 221 reserved. 222 """ 223 224 if temp_position is not None: 225 pass 226 elif not self.temp_positions: 227 temp_position = 0 228 else: 229 temp_position = max(self.temp_positions) + 1 230 231 self.temp_positions.add(temp_position) 232 self.max_temp_position = max(self.max_temp_position, temp_position) 233 return self.unit.all_local_usage + temp_position # position in frame 234 235 def ensure_temp(self, instruction=None): 236 237 """ 238 Ensure that the 'instruction' is using a reserved temporary storage 239 position. 240 """ 241 242 if isinstance(instruction, LoadTemp): 243 temp_position = instruction.attr - self.unit.all_local_usage 244 self.reserve_temp(temp_position) 245 246 def discard_temp(self, instruction=None): 247 248 "Discard any temporary storage position used by 'instruction'." 249 250 if isinstance(instruction, LoadTemp): 251 temp_position = instruction.attr - self.unit.all_local_usage 252 self.free_temp(temp_position) 253 254 def free_temp(self, temp_position): 255 256 "Free the temporary storage position specified by 'temp_position'." 257 258 if temp_position in self.temp_positions: 259 self.temp_positions.remove(temp_position) 260 261 def set_frame_usage(self, node, extend): 262 263 """ 264 Ensure that the frame usage for the unit associated with 'node' is set 265 on the 'extend' instruction. 266 """ 267 268 # Remove any ExtendFrame instructions which do nothing. 269 270 if self.last_op() is extend: 271 self.remove_op() 272 return 273 274 ntemp = self.max_temp_position + 1 275 extend.attr = ntemp + node.unit.local_usage # NOTE: See get_code for similar code. 276 277 # Code writing methods. 278 279 def new_op(self, op): 280 281 """ 282 Add 'op' to the generated code, returning a true value if an instruction 283 was added. 284 """ 285 286 # Optimise load operations employed by this instruction. 287 288 if self.optimiser.optimise_away_no_operations(op) or self.optimiser.optimise_unused_handlers(op): 289 return 0 290 291 # Add the operation to the current block. 292 293 self.blocks[-1].code.append(op) 294 self.optimiser.set_new(op) 295 return 1 296 297 def insert_op(self, i, op): 298 299 "Insert at index 'i' in the current block the instruction 'op'." 300 301 self.blocks[-1].code.insert(i, op) 302 303 def remove_op(self): 304 305 "Remove the last instruction." 306 307 op = self.blocks[-1].code.pop() 308 self.optimiser.clear_active() 309 310 def replace_op(self, op): 311 312 "Replace the last added instruction with 'op'." 313 314 self.remove_op() 315 self.new_op(op) 316 317 def replace_active_value(self, op): 318 319 """ 320 Replace the value-providing active instruction with 'op' if appropriate. 321 """ 322 323 self.optimiser.remove_active_value() 324 self.new_op(op) 325 326 def last_op(self): 327 328 "Return the last added instruction." 329 330 try: 331 return self.blocks[-1].code[-1] 332 except IndexError: 333 return None 334 335 # vim: tabstop=4 expandtab shiftwidth=4