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 is set to a false value, new temporary storage to 130 hold the recorded value will be allocated; 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_copy = expr.copy() 167 assign_op = self.last_op() 168 169 # Either insert the instruction yielding the value and adjust the 170 # assignment source. 171 172 expr_copy.target = "source" 173 174 if self.insert_op(-1, expr_copy): 175 assign_op.source = "source" 176 self.update_temp(expr, expr_copy) 177 178 # (Now, the instruction need not be inserted.) 179 180 # Or transfer the working value to the source register. 181 182 elif assign_op.working == "working": 183 self.insert_op(-1, Transfer(source="working_context", target="source_context")) 184 self.insert_op(-1, Transfer(source="working", target="source")) 185 assign_op.source = "source" 186 187 # Or let the assignment use the working register. 188 189 else: 190 assign_op.source = "working" 191 192 def set_target(self, target): 193 194 "Reset the target of the active instruction to 'target'." 195 196 self.optimiser.set_target(target) 197 198 def is_immediate_user(self, node): 199 200 """ 201 Return whether 'node' is an immediate user of an assignment expression. 202 """ 203 204 return isinstance(node, (compiler.ast.AssName, compiler.ast.AssAttr)) 205 206 def has_immediate_usage(self, nodes): 207 208 """ 209 Return whether 'nodes' are all immediate users of an assignment expression. 210 """ 211 212 for n in nodes: 213 if not self.is_immediate_user(n): 214 return 0 215 return 1 216 217 # Temporary storage administration. 218 219 def get_temp(self): 220 221 """ 222 Return a temporary storage access instruction for the current value. 223 Initially, this instruction is not associated with an allocated unit of 224 temporary storage, and if used as a new instruction will not be added to 225 the code, but if the current value changes, the 'set_temp' method will 226 be called by the optimiser and a unit of storage will be allocated. 227 """ 228 229 temp = LoadTemp(None) 230 self.optimiser.request_active_value(temp, self.blocks[-1], len(self.blocks[-1].code)) 231 return temp 232 233 def set_temp(self, temp, block, pos): 234 235 """ 236 Emit a storage instruction for the given 'temp' loading instruction, 237 reserving a new temporary storage location. 238 """ 239 240 if temp.attr is None: 241 temp.attr = self.reserve_temp() 242 block.insert(pos, StoreTemp(temp.attr)) 243 244 def update_temp(self, temp, updated): 245 246 "Update 'temp' using the given 'updated' instruction." 247 248 if isinstance(temp, LoadTemp): 249 temp.attr = updated.attr 250 251 def reserve_temp(self, temp_position=None): 252 253 """ 254 Reserve a new temporary storage position, or if the optional 255 'temp_position' is specified, ensure that this particular position is 256 reserved. 257 """ 258 259 if temp_position is not None: 260 pass 261 elif not self.temp_positions: 262 temp_position = 0 263 else: 264 temp_position = max(self.temp_positions) + 1 265 266 self.temp_positions.add(temp_position) 267 self.max_temp_position = max(self.max_temp_position, temp_position) 268 return self.unit.all_local_usage + temp_position # position in frame 269 270 def ensure_temp(self, instruction=None): 271 272 """ 273 Ensure that the 'instruction' is using a reserved temporary storage 274 position. 275 """ 276 277 if isinstance(instruction, LoadTemp): 278 temp_position = instruction.attr - self.unit.all_local_usage 279 self.reserve_temp(temp_position) 280 281 def discard_temp(self, instruction=None): 282 283 "Discard any temporary storage position used by 'instruction'." 284 285 if isinstance(instruction, LoadTemp) and instruction.attr is not None: 286 temp_position = instruction.attr - self.unit.all_local_usage 287 self.free_temp(temp_position) 288 self.optimiser.ignore_active_value() 289 290 def free_temp(self, temp_position): 291 292 "Free the temporary storage position specified by 'temp_position'." 293 294 if temp_position in self.temp_positions: 295 self.temp_positions.remove(temp_position) 296 297 def set_frame_usage(self, node, extend): 298 299 """ 300 Ensure that the frame usage for the unit associated with 'node' is set 301 on the 'extend' instruction. 302 """ 303 304 # Remove any ExtendFrame instructions which do nothing. 305 306 if self.last_op() is extend: 307 self.remove_op() 308 return 309 310 ntemp = self.max_temp_position + 1 311 extend.attr = ntemp + node.unit.local_usage # NOTE: See get_code for similar code. 312 313 # Code writing methods. 314 315 def new_op(self, op): 316 317 """ 318 Add 'op' to the generated code, returning a true value if an instruction 319 was added. 320 """ 321 322 if not self.check_op(op): 323 return 0 324 325 # Add the operation to the current block. 326 327 self.optimiser.set_new(op) 328 self.blocks[-1].append(op) 329 return 1 330 331 def insert_op(self, i, op): 332 333 "Insert at index 'i' in the current block the instruction 'op'." 334 335 if not self.check_op(op): 336 return 0 337 338 self.blocks[-1].insert(i, op) 339 return 1 340 341 def check_op(self, op): 342 343 "Return whether the given 'op' is to be added to the code." 344 345 # Optimise away temporary storage instructions where the active value is 346 # still available and was not recorded. 347 348 if isinstance(op, LoadTemp) and op.attr is None: 349 return 0 350 351 # Optimise load operations employed by this instruction. 352 353 if self.optimiser.optimise_away_no_operations(op) or self.optimiser.optimise_unused_handlers(op): 354 return 0 355 356 return 1 357 358 def remove_op(self): 359 360 "Remove the last instruction." 361 362 op = self.blocks[-1].code.pop() 363 self.optimiser.clear_active() 364 365 def replace_op(self, op): 366 367 "Replace the last added instruction with 'op'." 368 369 self.remove_op() 370 self.new_op(op) 371 372 def replace_active_value(self, op): 373 374 """ 375 Replace the value-providing active instruction with 'op' if appropriate. 376 """ 377 378 self.optimiser.remove_active_value() 379 self.new_op(op) 380 381 def last_op(self): 382 383 "Return the last added instruction." 384 385 try: 386 return self.blocks[-1].code[-1] 387 except IndexError: 388 return None 389 390 # vim: tabstop=4 expandtab shiftwidth=4