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): 139 140 """ 141 Set the source of an assignment using the current assignment value. This 142 sets the source input for the current instruction. 143 """ 144 145 self.optimiser.set_source(self.expr_temp[-1]) 146 147 # Optimise away constant storage if appropriate. 148 149 if self.optimiser.optimise_constant_storage(): 150 self.remove_op() 151 152 def is_immediate_user(self, node): 153 154 """ 155 Return whether 'node' is an immediate user of an assignment expression. 156 """ 157 158 return isinstance(node, (compiler.ast.AssName, compiler.ast.AssAttr)) 159 160 def has_immediate_usage(self, nodes): 161 162 """ 163 Return whether 'nodes' are all immediate users of an assignment expression. 164 """ 165 166 for n in nodes: 167 if not self.is_immediate_user(n): 168 return 0 169 return 1 170 171 # Temporary storage administration. 172 173 def get_temp(self): 174 175 """ 176 Add a temporary storage instruction for the current value and return a 177 sequence of access instructions. 178 """ 179 180 position_in_frame = self.reserve_temp() 181 self.new_op(StoreTemp(position_in_frame)) 182 return LoadTemp(position_in_frame) 183 184 def reserve_temp(self, temp_position=None): 185 186 """ 187 Reserve a new temporary storage position, or if the optional 188 'temp_position' is specified, ensure that this particular position is 189 reserved. 190 """ 191 192 if temp_position is not None: 193 pass 194 elif not self.temp_positions: 195 temp_position = 0 196 else: 197 temp_position = max(self.temp_positions) + 1 198 199 self.temp_positions.add(temp_position) 200 self.max_temp_position = max(self.max_temp_position, temp_position) 201 return self.unit.all_local_usage + temp_position # position in frame 202 203 def ensure_temp(self, instruction=None): 204 205 """ 206 Ensure that the 'instruction' is using a reserved temporary storage 207 position. 208 """ 209 210 if isinstance(instruction, LoadTemp): 211 temp_position = instruction.attr - self.unit.all_local_usage 212 self.reserve_temp(temp_position) 213 214 def discard_temp(self, instruction=None): 215 216 "Discard any temporary storage position used by 'instruction'." 217 218 if isinstance(instruction, LoadTemp): 219 temp_position = instruction.attr - self.unit.all_local_usage 220 self.free_temp(temp_position) 221 222 def free_temp(self, temp_position): 223 224 "Free the temporary storage position specified by 'temp_position'." 225 226 if temp_position in self.temp_positions: 227 self.temp_positions.remove(temp_position) 228 229 def set_frame_usage(self, node, extend): 230 231 """ 232 Ensure that the frame usage for the unit associated with 'node' is set 233 on the 'extend' instruction. 234 """ 235 236 # Remove any ExtendFrame instructions which do nothing. 237 238 if self.last_op() is extend: 239 self.remove_op() 240 return 241 242 ntemp = self.max_temp_position + 1 243 extend.attr = ntemp + node.unit.local_usage # NOTE: See get_code for similar code. 244 245 # Code writing methods. 246 247 def new_op(self, op): 248 249 """ 250 Add 'op' to the generated code, returning a true value if an instruction 251 was added. 252 """ 253 254 # Optimise load operations employed by this instruction. 255 256 self.optimiser.optimise_load_operations(op) 257 if self.optimiser.optimise_away_no_operations(op) or self.optimiser.optimise_unused_handlers(op): 258 return 0 259 260 # Add the operation to the current block. 261 262 self.blocks[-1].code.append(op) 263 self.optimiser.set_new(op) 264 return 1 265 266 def remove_op(self): 267 268 "Remove the last instruction." 269 270 op = self.blocks[-1].code.pop() 271 self.optimiser.clear_active() 272 273 def replace_op(self, op): 274 275 "Replace the last added instruction with 'op'." 276 277 self.remove_op() 278 self.new_op(op) 279 280 def replace_active_value(self, op): 281 282 """ 283 Replace the value-providing active instruction with 'op' if appropriate. 284 """ 285 286 self.optimiser.remove_active_value() 287 self.new_op(op) 288 289 def last_op(self): 290 291 "Return the last added instruction." 292 293 try: 294 return self.blocks[-1].code[-1] 295 except IndexError: 296 return None 297 298 # vim: tabstop=4 expandtab shiftwidth=4