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 # The start of the current assignment target instructions. 49 50 self.target_start = None 51 52 # Wiring within the code. 53 54 self.labels = {} 55 self.label_number = 0 56 self.loop_blocks = [] 57 self.exception_blocks = [] 58 59 def reset(self): 60 61 "Reset the state of the assembler." 62 63 # The code itself. This is limited to the code for a particular block 64 # being processed. 65 66 self.blocks = [] 67 68 # Information about temporary values. 69 70 self.temp_positions = set() 71 self.max_temp_position = -1 72 73 # Information about instructions which construct frames. 74 75 self.frame_makers = [] 76 77 # Optimiser state must be reset for each unit. 78 79 self.optimiser.reset() 80 81 def new_block(self): 82 83 "Return a new code block." 84 85 return Block() 86 87 def get_block(self): 88 89 "Return the current block." 90 91 return self.blocks[-1] 92 93 def set_block(self, block, preceding=None): 94 95 """ 96 Add the given 'block' to the unit's list of blocks, noting any active 97 value information from 'preceding' blocks on the new block. 98 """ 99 100 self.optimiser.reset(block, preceding) 101 self.blocks.append(block) 102 103 def get_loop_blocks(self): 104 return self.loop_blocks[-1] 105 106 def add_loop_blocks(self, next_block, exit_block): 107 self.loop_blocks.append((next_block, exit_block)) 108 109 def drop_loop_blocks(self): 110 self.loop_blocks.pop() 111 112 def add_exception_unit(self): 113 self.exception_blocks.append([]) 114 115 def get_exception_blocks(self): 116 return self.exception_blocks[-1][-1] 117 118 def add_exception_blocks(self, handler_block, exit_block): 119 self.exception_blocks[-1].append((handler_block, exit_block)) 120 121 def drop_exception_blocks(self): 122 self.exception_blocks[-1].pop() 123 124 def drop_exception_unit(self): 125 self.exception_blocks.pop() 126 127 # Assignment expression values. 128 129 def record_value(self, immediate=1): 130 131 """ 132 Record the current active value for an assignment. If the optional 133 'immediate' parameter is set to a false value, new temporary storage to 134 hold the recorded value will be allocated; otherwise, the 135 value-providing instruction may be replicated in order to provide the 136 active value later on. 137 """ 138 139 if immediate: 140 temp = self.optimiser.optimise_temp_storage() 141 else: 142 temp = self.get_temp() 143 self.expr_temp.append(temp) 144 145 def discard_value(self): 146 147 "Discard any temporary storage in use for the current assignment value." 148 149 self.discard_temp(self.expr_temp.pop()) 150 151 def start_target(self): 152 153 """ 154 Start recording instructions used to define the target of an assignment. 155 """ 156 157 self.target_start = len(self.blocks[-1]) 158 159 def remove_target_ops(self): 160 161 "Remove the assignment target instructions." 162 163 del self.blocks[-1].code[self.target_start:] 164 self.target_start = None 165 self.optimiser.clear_active() 166 167 def get_target_ops(self): 168 169 "Return the assignment target instructions." 170 171 return self.blocks[-1].code[self.target_start:] 172 173 def assign_value(self, expr=None): 174 175 """ 176 Set the source of an assignment using 'expr' or the current assignment 177 value. This may set the source register of the current instruction. 178 """ 179 180 expr = expr or self.expr_temp[-1] 181 182 # Optimise away constant storage if appropriate. 183 184 if self.optimiser.optimise_constant_storage(expr): 185 self.remove_target_ops() 186 return 187 188 # Otherwise, insert the assignment source. 189 190 expr_copy = expr.copy() 191 assign_ops = self.get_target_ops() 192 193 if not assign_ops: 194 self.target_start = None 195 return 196 197 assign_op = assign_ops[-1] 198 199 # Either insert the instruction yielding the value and adjust the 200 # assignment source. 201 202 expr_copy.target = "source" 203 204 if self.insert_op(-1, expr_copy): 205 assign_op.source = "source" 206 self.update_temp(expr, expr_copy) 207 208 # (Now, the instruction need not be inserted.) 209 210 # Or transfer the working value to the source register. 211 212 elif assign_op.working == "working": 213 self.insert_op(-1, Transfer(source="working_context", target="source_context")) 214 self.insert_op(-1, Transfer(source="working", target="source")) 215 assign_op.source = "source" 216 217 # Or let the assignment use the working register. 218 219 else: 220 assign_op.source = "working" 221 222 self.target_start = None 223 224 def set_target(self, target): 225 226 "Reset the target of the active instruction to 'target'." 227 228 self.optimiser.set_target(target) 229 230 def is_immediate_user(self, node): 231 232 """ 233 Return whether 'node' is an immediate user of an assignment expression. 234 """ 235 236 return isinstance(node, (compiler.ast.AssName, compiler.ast.AssAttr)) 237 238 def has_immediate_usage(self, nodes): 239 240 """ 241 Return whether 'nodes' are all immediate users of an assignment expression. 242 """ 243 244 for n in nodes: 245 if not self.is_immediate_user(n): 246 return 0 247 return 1 248 249 # Temporary storage administration. 250 251 def get_temp(self): 252 253 """ 254 Return a temporary storage access instruction for the current value. 255 Initially, this instruction is not associated with an allocated unit of 256 temporary storage, and if used as a new instruction will not be added to 257 the code, but if the current value changes, the 'set_temp' method will 258 be called by the optimiser and a unit of storage will be allocated. 259 """ 260 261 temp = LoadTemp(None) 262 self.optimiser.request_active_value(temp, self.blocks[-1], len(self.blocks[-1].code)) 263 return temp 264 265 def set_temp(self, temp, block, pos): 266 267 """ 268 Emit a storage instruction for the given 'temp' loading instruction, 269 reserving a new temporary storage location. 270 """ 271 272 if temp.attr is None: 273 temp.attr = self.reserve_temp() 274 block.insert(pos, StoreTemp(temp.attr)) 275 276 def update_temp(self, temp, updated): 277 278 "Update 'temp' using the given 'updated' instruction." 279 280 if isinstance(temp, LoadTemp): 281 temp.attr = updated.attr 282 283 def reserve_temp(self, temp_position=None): 284 285 """ 286 Reserve a new temporary storage position, or if the optional 287 'temp_position' is specified, ensure that this particular position is 288 reserved. 289 """ 290 291 if temp_position is not None: 292 pass 293 elif not self.temp_positions: 294 temp_position = 0 295 else: 296 temp_position = max(self.temp_positions) + 1 297 298 self.temp_positions.add(temp_position) 299 self.max_temp_position = max(self.max_temp_position, temp_position) 300 return self.unit.all_local_usage + temp_position # position in frame 301 302 def ensure_temp(self, instruction=None): 303 304 """ 305 Ensure that the 'instruction' is using a reserved temporary storage 306 position. 307 """ 308 309 if isinstance(instruction, LoadTemp): 310 temp_position = instruction.attr - self.unit.all_local_usage 311 self.reserve_temp(temp_position) 312 313 def discard_temp(self, instruction=None): 314 315 "Discard any temporary storage position used by 'instruction'." 316 317 if isinstance(instruction, LoadTemp) and instruction.attr is not None: 318 temp_position = instruction.attr - self.unit.all_local_usage 319 self.free_temp(temp_position) 320 321 # Prevent any requested active value from generating instructions now 322 # that our interest in it has passed. 323 324 self.optimiser.ignore_active_value() 325 326 def free_temp(self, temp_position): 327 328 "Free the temporary storage position specified by 'temp_position'." 329 330 if temp_position in self.temp_positions: 331 self.temp_positions.remove(temp_position) 332 333 def set_frame_usage(self, node, extend): 334 335 """ 336 Ensure that the frame usage for the unit associated with 'node' is set 337 on the 'extend' instruction. 338 """ 339 340 # Remove any ExtendFrame instructions which do nothing. 341 342 if self.last_op() is extend: 343 self.remove_op() 344 return 345 346 ntemp = self.max_temp_position + 1 347 extend.attr = ntemp + node.unit.local_usage # NOTE: See get_code for similar code. 348 349 # Code writing methods. 350 351 def new_op(self, op): 352 353 """ 354 Add 'op' to the generated code, returning a true value if an instruction 355 was added. 356 """ 357 358 if not self.check_op(op): 359 return 0 360 361 # Add the operation to the current block. 362 363 self.optimiser.set_new(op) 364 self.blocks[-1].append(op) 365 return 1 366 367 def insert_op(self, i, op): 368 369 "Insert at index 'i' in the current block the instruction 'op'." 370 371 if not self.check_op(op): 372 return 0 373 374 self.blocks[-1].insert(i, op) 375 return 1 376 377 def check_op(self, op): 378 379 "Return whether the given 'op' is to be added to the code." 380 381 # Optimise away temporary storage instructions where the active value is 382 # still available and was not recorded. 383 384 if isinstance(op, LoadTemp) and op.attr is None: 385 return 0 386 387 # Optimise load operations employed by this instruction. 388 389 if self.optimiser.optimise_away_no_operations(op) or self.optimiser.optimise_unused_handlers(op): 390 return 0 391 392 return 1 393 394 def remove_op(self): 395 396 "Remove the last instruction." 397 398 op = self.blocks[-1].code.pop() 399 self.optimiser.clear_active() 400 return op 401 402 def replace_op(self, op): 403 404 "Replace the last added instruction with 'op'." 405 406 self.remove_op() 407 self.new_op(op) 408 409 def replace_active_value(self, op): 410 411 """ 412 Replace the value-providing active instruction with 'op' if appropriate. 413 """ 414 415 removed = self.optimiser.remove_active_value() 416 self.new_op(op) 417 return removed 418 419 def last_op(self): 420 421 "Return the last added instruction." 422 423 try: 424 return self.blocks[-1].code[-1] 425 except IndexError: 426 return None 427 428 # vim: tabstop=4 expandtab shiftwidth=4