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