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 set_block(self, block): 84 85 "Add the given 'block' to the unit's list of blocks." 86 87 self.optimiser.reset() 88 self.blocks.append(block) 89 90 def get_loop_blocks(self): 91 return self.loop_blocks[-1] 92 93 def add_loop_blocks(self, next_block, exit_block): 94 self.loop_blocks.append((next_block, exit_block)) 95 96 def drop_loop_blocks(self): 97 self.loop_blocks.pop() 98 99 def add_exception_unit(self): 100 self.exception_blocks.append([]) 101 102 def get_exception_blocks(self): 103 return self.exception_blocks[-1][-1] 104 105 def add_exception_blocks(self, handler_block, exit_block): 106 self.exception_blocks[-1].append((handler_block, exit_block)) 107 108 def drop_exception_blocks(self): 109 self.exception_blocks[-1].pop() 110 111 def drop_exception_unit(self): 112 self.exception_blocks.pop() 113 114 # Assignment expression values. 115 116 def record_value(self, immediate=1): 117 118 """ 119 Record the current active value for an assignment. If the optional 120 'immediate' parameter if set to a false value always allocates new 121 temporary storage to hold the recorded value; otherwise, the 122 value-providing instruction may be replicated in order to provide the 123 active value later on. 124 """ 125 126 if immediate: 127 temp = self.optimiser.optimise_temp_storage() 128 else: 129 temp = self.get_temp() 130 self.expr_temp.append(temp) 131 132 def discard_value(self): 133 134 "Discard any temporary storage in use for the current assignment value." 135 136 self.discard_temp(self.expr_temp.pop()) 137 138 def set_source(self, expr=None): 139 140 """ 141 Set the source of an assignment using 'expr' or the current assignment 142 value. This sets the source register of the current instruction. 143 """ 144 145 if expr is None: 146 expr = self.expr_temp[-1] 147 148 # Optimise away constant storage if appropriate. 149 150 if self.optimiser.optimise_constant_storage(expr): 151 self.remove_op() 152 return 153 154 # Otherwise, insert the assignment source. 155 156 if expr is not None: 157 expr = expr.copy() 158 expr.target = "source" 159 self.insert_op(-1, expr) 160 self.last_op().source = "source" 161 162 def set_working(self, expr): 163 if expr is not None: 164 expr.target = "working" 165 self.insert_op(-1, expr) 166 self.last_op().source = "working" 167 168 def set_target(self, target): 169 170 "Reset the target of the active instruction to 'target'." 171 172 self.optimiser.set_target(target) 173 174 def is_immediate_user(self, node): 175 176 """ 177 Return whether 'node' is an immediate user of an assignment expression. 178 """ 179 180 return isinstance(node, (compiler.ast.AssName, compiler.ast.AssAttr)) 181 182 def has_immediate_usage(self, nodes): 183 184 """ 185 Return whether 'nodes' are all immediate users of an assignment expression. 186 """ 187 188 for n in nodes: 189 if not self.is_immediate_user(n): 190 return 0 191 return 1 192 193 # Temporary storage administration. 194 195 def get_temp(self): 196 197 """ 198 Add a temporary storage instruction for the current value and return a 199 sequence of access instructions. 200 """ 201 202 position_in_frame = self.reserve_temp() 203 self.new_op(StoreTemp(position_in_frame)) 204 return LoadTemp(position_in_frame) 205 206 def reserve_temp(self, temp_position=None): 207 208 """ 209 Reserve a new temporary storage position, or if the optional 210 'temp_position' is specified, ensure that this particular position is 211 reserved. 212 """ 213 214 if temp_position is not None: 215 pass 216 elif not self.temp_positions: 217 temp_position = 0 218 else: 219 temp_position = max(self.temp_positions) + 1 220 221 self.temp_positions.add(temp_position) 222 self.max_temp_position = max(self.max_temp_position, temp_position) 223 return self.unit.all_local_usage + temp_position # position in frame 224 225 def ensure_temp(self, instruction=None): 226 227 """ 228 Ensure that the 'instruction' is using a reserved temporary storage 229 position. 230 """ 231 232 if isinstance(instruction, LoadTemp): 233 temp_position = instruction.attr - self.unit.all_local_usage 234 self.reserve_temp(temp_position) 235 236 def discard_temp(self, instruction=None): 237 238 "Discard any temporary storage position used by 'instruction'." 239 240 if isinstance(instruction, LoadTemp): 241 temp_position = instruction.attr - self.unit.all_local_usage 242 self.free_temp(temp_position) 243 244 def free_temp(self, temp_position): 245 246 "Free the temporary storage position specified by 'temp_position'." 247 248 if temp_position in self.temp_positions: 249 self.temp_positions.remove(temp_position) 250 251 def set_frame_usage(self, node, extend): 252 253 """ 254 Ensure that the frame usage for the unit associated with 'node' is set 255 on the 'extend' instruction. 256 """ 257 258 # Remove any ExtendFrame instructions which do nothing. 259 260 if self.last_op() is extend: 261 self.remove_op() 262 return 263 264 ntemp = self.max_temp_position + 1 265 extend.attr = ntemp + node.unit.local_usage # NOTE: See get_code for similar code. 266 267 # Code writing methods. 268 269 def new_op(self, op): 270 271 """ 272 Add 'op' to the generated code, returning a true value if an instruction 273 was added. 274 """ 275 276 # Optimise load operations employed by this instruction. 277 278 if self.optimiser.optimise_away_no_operations(op) or self.optimiser.optimise_unused_handlers(op): 279 return 0 280 281 # Add the operation to the current block. 282 283 self.blocks[-1].code.append(op) 284 self.optimiser.set_new(op) 285 return 1 286 287 def insert_op(self, i, op): 288 289 "Insert at index 'i' in the current block the instruction 'op'." 290 291 self.blocks[-1].code.insert(i, op) 292 293 def remove_op(self): 294 295 "Remove the last instruction." 296 297 op = self.blocks[-1].code.pop() 298 self.optimiser.clear_active() 299 300 def replace_op(self, op): 301 302 "Replace the last added instruction with 'op'." 303 304 self.remove_op() 305 self.new_op(op) 306 307 def replace_active_value(self, op): 308 309 """ 310 Replace the value-providing active instruction with 'op' if appropriate. 311 """ 312 313 self.optimiser.remove_active_value() 314 self.new_op(op) 315 316 def last_op(self): 317 318 "Return the last added instruction." 319 320 try: 321 return self.blocks[-1].code[-1] 322 except IndexError: 323 return None 324 325 # vim: tabstop=4 expandtab shiftwidth=4