1.1 --- a/micropython/ast.py Sun Jun 26 23:54:37 2011 +0200
1.2 +++ b/micropython/ast.py Sat Jul 02 02:41:33 2011 +0200
1.3 @@ -19,18 +19,18 @@
1.4 this program. If not, see <http://www.gnu.org/licenses/>.
1.5 """
1.6
1.7 -from micropython.opt import Optimiser
1.8 from micropython.common import *
1.9 from micropython.data import *
1.10 from micropython.rsvp import *
1.11 from micropython.trans import Helper
1.12 +from micropython.code import Assembler
1.13 import compiler.ast
1.14
1.15 # Program visitors.
1.16
1.17 -class Translation(ASTVisitor, Helper):
1.18 +class Translation(ASTVisitor, Assembler, Helper):
1.19
1.20 - "A translated module."
1.21 + "A module translator."
1.22
1.23 # Attribute access instructions, for use with the appropriate handlers.
1.24
1.25 @@ -53,6 +53,7 @@
1.26 """
1.27
1.28 ASTVisitor.__init__(self)
1.29 + Assembler.__init__(self, program.optimisations)
1.30 self.visitor = self
1.31 self.module = module
1.32
1.33 @@ -64,54 +65,18 @@
1.34 self.importer = self.program.get_importer()
1.35 self.builtins = self.importer.modules.get("__builtins__")
1.36
1.37 - # Optimisation.
1.38 -
1.39 - self.optimiser = Optimiser(self, program.optimisations)
1.40 -
1.41 - # The current unit being translated.
1.42 -
1.43 - self.unit = None
1.44 + # Status flags.
1.45
1.46 - # The temporary storage used by the current assignment expression.
1.47 -
1.48 - self.expr_temp = []
1.49 -
1.50 - # Wiring within the code.
1.51 -
1.52 - self.labels = {}
1.53 - self.label_number = 0
1.54 - self.loop_blocks = []
1.55 - self.exception_blocks = []
1.56 self.in_exception_handler = 0
1.57 self.in_assignment = 0 # for slicing and subscript
1.58
1.59 + # Reset the assembler.
1.60 +
1.61 self.reset()
1.62
1.63 def __repr__(self):
1.64 return "Translation(%r)" % self.module
1.65
1.66 - def reset(self):
1.67 -
1.68 - "Reset the state of the translator."
1.69 -
1.70 - # The code itself. This is limited to the code for a particular block
1.71 - # being processed.
1.72 -
1.73 - self.blocks = []
1.74 -
1.75 - # Information about temporary values.
1.76 -
1.77 - self.temp_positions = set()
1.78 - self.max_temp_position = -1
1.79 -
1.80 - # Information about instructions which construct frames.
1.81 -
1.82 - self.frame_makers = []
1.83 -
1.84 - # Optimiser state must be reset for each unit.
1.85 -
1.86 - self.optimiser.reset()
1.87 -
1.88 def get_module_code(self):
1.89
1.90 """
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/micropython/code.py Sat Jul 02 02:41:33 2011 +0200
2.3 @@ -0,0 +1,298 @@
2.4 +#!/usr/bin/env python
2.5 +
2.6 +"""
2.7 +Generate low-level code.
2.8 +
2.9 +Copyright (C) 2007, 2008, 2009, 2010, 2011 Paul Boddie <paul@boddie.org.uk>
2.10 +
2.11 +This program is free software; you can redistribute it and/or modify it under
2.12 +the terms of the GNU General Public License as published by the Free Software
2.13 +Foundation; either version 3 of the License, or (at your option) any later
2.14 +version.
2.15 +
2.16 +This program is distributed in the hope that it will be useful, but WITHOUT
2.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
2.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
2.19 +details.
2.20 +
2.21 +You should have received a copy of the GNU General Public License along with
2.22 +this program. If not, see <http://www.gnu.org/licenses/>.
2.23 +"""
2.24 +
2.25 +from micropython.opt import Optimiser
2.26 +from micropython.common import *
2.27 +from micropython.data import *
2.28 +from micropython.rsvp import *
2.29 +import compiler.ast
2.30 +
2.31 +class Assembler:
2.32 +
2.33 + "Support for generating low-level code."
2.34 +
2.35 + def __init__(self, optimisations):
2.36 +
2.37 + "Initialise the assembler with an optimiser and status attributes."
2.38 +
2.39 + # Optimisation.
2.40 +
2.41 + self.optimiser = Optimiser(self, optimisations)
2.42 +
2.43 + # The current unit being translated.
2.44 +
2.45 + self.unit = None
2.46 +
2.47 + # The temporary storage used by the current assignment expression.
2.48 +
2.49 + self.expr_temp = []
2.50 +
2.51 + # Wiring within the code.
2.52 +
2.53 + self.labels = {}
2.54 + self.label_number = 0
2.55 + self.loop_blocks = []
2.56 + self.exception_blocks = []
2.57 +
2.58 + def reset(self):
2.59 +
2.60 + "Reset the state of the assembler."
2.61 +
2.62 + # The code itself. This is limited to the code for a particular block
2.63 + # being processed.
2.64 +
2.65 + self.blocks = []
2.66 +
2.67 + # Information about temporary values.
2.68 +
2.69 + self.temp_positions = set()
2.70 + self.max_temp_position = -1
2.71 +
2.72 + # Information about instructions which construct frames.
2.73 +
2.74 + self.frame_makers = []
2.75 +
2.76 + # Optimiser state must be reset for each unit.
2.77 +
2.78 + self.optimiser.reset()
2.79 +
2.80 + def new_block(self):
2.81 +
2.82 + "Return a new code block."
2.83 +
2.84 + return Block()
2.85 +
2.86 + def set_block(self, block):
2.87 +
2.88 + "Add the given 'block' to the unit's list of blocks."
2.89 +
2.90 + self.optimiser.reset()
2.91 + self.blocks.append(block)
2.92 +
2.93 + def get_loop_blocks(self):
2.94 + return self.loop_blocks[-1]
2.95 +
2.96 + def add_loop_blocks(self, next_block, exit_block):
2.97 + self.loop_blocks.append((next_block, exit_block))
2.98 +
2.99 + def drop_loop_blocks(self):
2.100 + self.loop_blocks.pop()
2.101 +
2.102 + def add_exception_unit(self):
2.103 + self.exception_blocks.append([])
2.104 +
2.105 + def get_exception_blocks(self):
2.106 + return self.exception_blocks[-1][-1]
2.107 +
2.108 + def add_exception_blocks(self, handler_block, exit_block):
2.109 + self.exception_blocks[-1].append((handler_block, exit_block))
2.110 +
2.111 + def drop_exception_blocks(self):
2.112 + self.exception_blocks[-1].pop()
2.113 +
2.114 + def drop_exception_unit(self):
2.115 + self.exception_blocks.pop()
2.116 +
2.117 + # Assignment expression values.
2.118 +
2.119 + def record_value(self, immediate=1):
2.120 +
2.121 + """
2.122 + Record the current active value for an assignment. If the optional
2.123 + 'immediate' parameter if set to a false value always allocates new
2.124 + temporary storage to hold the recorded value; otherwise, the
2.125 + value-providing instruction may be replicated in order to provide the
2.126 + active value later on.
2.127 + """
2.128 +
2.129 + if immediate:
2.130 + temp = self.optimiser.optimise_temp_storage()
2.131 + else:
2.132 + temp = self.get_temp()
2.133 + self.expr_temp.append(temp)
2.134 +
2.135 + def discard_value(self):
2.136 +
2.137 + "Discard any temporary storage in use for the current assignment value."
2.138 +
2.139 + self.discard_temp(self.expr_temp.pop())
2.140 +
2.141 + def set_source(self):
2.142 +
2.143 + """
2.144 + Set the source of an assignment using the current assignment value. This
2.145 + sets the source input for the current instruction.
2.146 + """
2.147 +
2.148 + self.optimiser.set_source(self.expr_temp[-1])
2.149 +
2.150 + # Optimise away constant storage if appropriate.
2.151 +
2.152 + if self.optimiser.optimise_constant_storage():
2.153 + self.remove_op()
2.154 +
2.155 + def is_immediate_user(self, node):
2.156 +
2.157 + """
2.158 + Return whether 'node' is an immediate user of an assignment expression.
2.159 + """
2.160 +
2.161 + return isinstance(node, (compiler.ast.AssName, compiler.ast.AssAttr))
2.162 +
2.163 + def has_immediate_usage(self, nodes):
2.164 +
2.165 + """
2.166 + Return whether 'nodes' are all immediate users of an assignment expression.
2.167 + """
2.168 +
2.169 + for n in nodes:
2.170 + if not self.is_immediate_user(n):
2.171 + return 0
2.172 + return 1
2.173 +
2.174 + # Temporary storage administration.
2.175 +
2.176 + def get_temp(self):
2.177 +
2.178 + """
2.179 + Add a temporary storage instruction for the current value and return a
2.180 + sequence of access instructions.
2.181 + """
2.182 +
2.183 + position_in_frame = self.reserve_temp()
2.184 + self.new_op(StoreTemp(position_in_frame))
2.185 + return LoadTemp(position_in_frame)
2.186 +
2.187 + def reserve_temp(self, temp_position=None):
2.188 +
2.189 + """
2.190 + Reserve a new temporary storage position, or if the optional
2.191 + 'temp_position' is specified, ensure that this particular position is
2.192 + reserved.
2.193 + """
2.194 +
2.195 + if temp_position is not None:
2.196 + pass
2.197 + elif not self.temp_positions:
2.198 + temp_position = 0
2.199 + else:
2.200 + temp_position = max(self.temp_positions) + 1
2.201 +
2.202 + self.temp_positions.add(temp_position)
2.203 + self.max_temp_position = max(self.max_temp_position, temp_position)
2.204 + return self.unit.all_local_usage + temp_position # position in frame
2.205 +
2.206 + def ensure_temp(self, instruction=None):
2.207 +
2.208 + """
2.209 + Ensure that the 'instruction' is using a reserved temporary storage
2.210 + position.
2.211 + """
2.212 +
2.213 + if isinstance(instruction, LoadTemp):
2.214 + temp_position = instruction.attr - self.unit.all_local_usage
2.215 + self.reserve_temp(temp_position)
2.216 +
2.217 + def discard_temp(self, instruction=None):
2.218 +
2.219 + "Discard any temporary storage position used by 'instruction'."
2.220 +
2.221 + if isinstance(instruction, LoadTemp):
2.222 + temp_position = instruction.attr - self.unit.all_local_usage
2.223 + self.free_temp(temp_position)
2.224 +
2.225 + def free_temp(self, temp_position):
2.226 +
2.227 + "Free the temporary storage position specified by 'temp_position'."
2.228 +
2.229 + if temp_position in self.temp_positions:
2.230 + self.temp_positions.remove(temp_position)
2.231 +
2.232 + def set_frame_usage(self, node, extend):
2.233 +
2.234 + """
2.235 + Ensure that the frame usage for the unit associated with 'node' is set
2.236 + on the 'extend' instruction.
2.237 + """
2.238 +
2.239 + # Remove any ExtendFrame instructions which do nothing.
2.240 +
2.241 + if self.last_op() is extend:
2.242 + self.remove_op()
2.243 + return
2.244 +
2.245 + ntemp = self.max_temp_position + 1
2.246 + extend.attr = ntemp + node.unit.local_usage # NOTE: See get_code for similar code.
2.247 +
2.248 + # Code writing methods.
2.249 +
2.250 + def new_op(self, op):
2.251 +
2.252 + """
2.253 + Add 'op' to the generated code, returning a true value if an instruction
2.254 + was added.
2.255 + """
2.256 +
2.257 + # Optimise load operations employed by this instruction.
2.258 +
2.259 + self.optimiser.optimise_load_operations(op)
2.260 + if self.optimiser.optimise_away_no_operations(op) or self.optimiser.optimise_unused_handlers(op):
2.261 + return 0
2.262 +
2.263 + # Add the operation to the current block.
2.264 +
2.265 + self.blocks[-1].code.append(op)
2.266 + self.optimiser.set_new(op)
2.267 + return 1
2.268 +
2.269 + def remove_op(self):
2.270 +
2.271 + "Remove the last instruction."
2.272 +
2.273 + op = self.blocks[-1].code.pop()
2.274 + self.optimiser.clear_active()
2.275 +
2.276 + def replace_op(self, op):
2.277 +
2.278 + "Replace the last added instruction with 'op'."
2.279 +
2.280 + self.remove_op()
2.281 + self.new_op(op)
2.282 +
2.283 + def replace_active_value(self, op):
2.284 +
2.285 + """
2.286 + Replace the value-providing active instruction with 'op' if appropriate.
2.287 + """
2.288 +
2.289 + self.optimiser.remove_active_value()
2.290 + self.new_op(op)
2.291 +
2.292 + def last_op(self):
2.293 +
2.294 + "Return the last added instruction."
2.295 +
2.296 + try:
2.297 + return self.blocks[-1].code[-1]
2.298 + except IndexError:
2.299 + return None
2.300 +
2.301 +# vim: tabstop=4 expandtab shiftwidth=4
3.1 --- a/micropython/trans.py Sun Jun 26 23:54:37 2011 +0200
3.2 +++ b/micropython/trans.py Sat Jul 02 02:41:33 2011 +0200
3.3 @@ -89,229 +89,6 @@
3.4 else:
3.5 raise TranslateError("No __builtins__ module is available for name %r." % name)
3.6
3.7 - # Code feature methods.
3.8 -
3.9 - def new_block(self):
3.10 -
3.11 - "Return a new code block."
3.12 -
3.13 - return Block()
3.14 -
3.15 - def set_block(self, block):
3.16 -
3.17 - "Add the given 'block' to the unit's list of blocks."
3.18 -
3.19 - self.optimiser.reset()
3.20 - self.blocks.append(block)
3.21 -
3.22 - def get_loop_blocks(self):
3.23 - return self.loop_blocks[-1]
3.24 -
3.25 - def add_loop_blocks(self, next_block, exit_block):
3.26 - self.loop_blocks.append((next_block, exit_block))
3.27 -
3.28 - def drop_loop_blocks(self):
3.29 - self.loop_blocks.pop()
3.30 -
3.31 - def add_exception_unit(self):
3.32 - self.exception_blocks.append([])
3.33 -
3.34 - def get_exception_blocks(self):
3.35 - return self.exception_blocks[-1][-1]
3.36 -
3.37 - def add_exception_blocks(self, handler_block, exit_block):
3.38 - self.exception_blocks[-1].append((handler_block, exit_block))
3.39 -
3.40 - def drop_exception_blocks(self):
3.41 - self.exception_blocks[-1].pop()
3.42 -
3.43 - def drop_exception_unit(self):
3.44 - self.exception_blocks.pop()
3.45 -
3.46 - # Assignment expression values.
3.47 -
3.48 - def record_value(self, immediate=1):
3.49 -
3.50 - """
3.51 - Record the current active value for an assignment. If the optional
3.52 - 'immediate' parameter if set to a false value always allocates new
3.53 - temporary storage to hold the recorded value; otherwise, the
3.54 - value-providing instruction may be replicated in order to provide the
3.55 - active value later on.
3.56 - """
3.57 -
3.58 - if immediate:
3.59 - temp = self.optimiser.optimise_temp_storage()
3.60 - else:
3.61 - temp = self.get_temp()
3.62 - self.expr_temp.append(temp)
3.63 -
3.64 - def discard_value(self):
3.65 -
3.66 - "Discard any temporary storage in use for the current assignment value."
3.67 -
3.68 - self.discard_temp(self.expr_temp.pop())
3.69 -
3.70 - def set_source(self):
3.71 -
3.72 - """
3.73 - Set the source of an assignment using the current assignment value. This
3.74 - sets the source input for the current instruction.
3.75 - """
3.76 -
3.77 - self.optimiser.set_source(self.expr_temp[-1])
3.78 -
3.79 - # Optimise away constant storage if appropriate.
3.80 -
3.81 - if self.optimiser.optimise_constant_storage():
3.82 - self.remove_op()
3.83 -
3.84 - def is_immediate_user(self, node):
3.85 -
3.86 - """
3.87 - Return whether 'node' is an immediate user of an assignment expression.
3.88 - """
3.89 -
3.90 - return isinstance(node, (compiler.ast.AssName, compiler.ast.AssAttr))
3.91 -
3.92 - def has_immediate_usage(self, nodes):
3.93 -
3.94 - """
3.95 - Return whether 'nodes' are all immediate users of an assignment expression.
3.96 - """
3.97 -
3.98 - for n in nodes:
3.99 - if not self.is_immediate_user(n):
3.100 - return 0
3.101 - return 1
3.102 -
3.103 - # Temporary storage administration.
3.104 -
3.105 - def get_temp(self):
3.106 -
3.107 - """
3.108 - Add a temporary storage instruction for the current value and return a
3.109 - sequence of access instructions.
3.110 - """
3.111 -
3.112 - position_in_frame = self.reserve_temp()
3.113 - self.new_op(StoreTemp(position_in_frame))
3.114 - return LoadTemp(position_in_frame)
3.115 -
3.116 - def reserve_temp(self, temp_position=None):
3.117 -
3.118 - """
3.119 - Reserve a new temporary storage position, or if the optional
3.120 - 'temp_position' is specified, ensure that this particular position is
3.121 - reserved.
3.122 - """
3.123 -
3.124 - if temp_position is not None:
3.125 - pass
3.126 - elif not self.temp_positions:
3.127 - temp_position = 0
3.128 - else:
3.129 - temp_position = max(self.temp_positions) + 1
3.130 -
3.131 - self.temp_positions.add(temp_position)
3.132 - self.max_temp_position = max(self.max_temp_position, temp_position)
3.133 - return self.unit.all_local_usage + temp_position # position in frame
3.134 -
3.135 - def ensure_temp(self, instruction=None):
3.136 -
3.137 - """
3.138 - Ensure that the 'instruction' is using a reserved temporary storage
3.139 - position.
3.140 - """
3.141 -
3.142 - if isinstance(instruction, LoadTemp):
3.143 - temp_position = instruction.attr - self.unit.all_local_usage
3.144 - self.reserve_temp(temp_position)
3.145 -
3.146 - def discard_temp(self, instruction=None):
3.147 -
3.148 - "Discard any temporary storage position used by 'instruction'."
3.149 -
3.150 - if isinstance(instruction, LoadTemp):
3.151 - temp_position = instruction.attr - self.unit.all_local_usage
3.152 - self.free_temp(temp_position)
3.153 -
3.154 - def free_temp(self, temp_position):
3.155 -
3.156 - "Free the temporary storage position specified by 'temp_position'."
3.157 -
3.158 - if temp_position in self.temp_positions:
3.159 - self.temp_positions.remove(temp_position)
3.160 -
3.161 - def set_frame_usage(self, node, extend):
3.162 -
3.163 - """
3.164 - Ensure that the frame usage for the unit associated with 'node' is set
3.165 - on the 'extend' instruction.
3.166 - """
3.167 -
3.168 - # Remove any ExtendFrame instructions which do nothing.
3.169 -
3.170 - if self.last_op() is extend:
3.171 - self.remove_op()
3.172 - return
3.173 -
3.174 - ntemp = self.max_temp_position + 1
3.175 - extend.attr = ntemp + node.unit.local_usage # NOTE: See get_code for similar code.
3.176 -
3.177 - # Code writing methods.
3.178 -
3.179 - def new_op(self, op):
3.180 -
3.181 - """
3.182 - Add 'op' to the generated code, returning a true value if an instruction
3.183 - was added.
3.184 - """
3.185 -
3.186 - # Optimise load operations employed by this instruction.
3.187 -
3.188 - self.optimiser.optimise_load_operations(op)
3.189 - if self.optimiser.optimise_away_no_operations(op) or self.optimiser.optimise_unused_handlers(op):
3.190 - return 0
3.191 -
3.192 - # Add the operation to the current block.
3.193 -
3.194 - self.blocks[-1].code.append(op)
3.195 - self.optimiser.set_new(op)
3.196 - return 1
3.197 -
3.198 - def remove_op(self):
3.199 -
3.200 - "Remove the last instruction."
3.201 -
3.202 - op = self.blocks[-1].code.pop()
3.203 - self.optimiser.clear_active()
3.204 -
3.205 - def replace_op(self, op):
3.206 -
3.207 - "Replace the last added instruction with 'op'."
3.208 -
3.209 - self.remove_op()
3.210 - self.new_op(op)
3.211 -
3.212 - def replace_active_value(self, op):
3.213 -
3.214 - """
3.215 - Replace the value-providing active instruction with 'op' if appropriate.
3.216 - """
3.217 -
3.218 - self.optimiser.remove_active_value()
3.219 - self.new_op(op)
3.220 -
3.221 - def last_op(self):
3.222 -
3.223 - "Return the last added instruction."
3.224 -
3.225 - try:
3.226 - return self.blocks[-1].code[-1]
3.227 - except IndexError:
3.228 - return None
3.229 -
3.230 # Common methods.
3.231
3.232 def _generateGuards(self, node):
3.233 @@ -1479,11 +1256,20 @@
3.234 for i, n in enumerate(node.nodes):
3.235 self.dispatch(n)
3.236 self.record_value()
3.237 - self.new_op(temp)
3.238 - self.new_op(StoreAttr(Attr(i + offset, None, None)))
3.239 - self.set_source()
3.240 + self._storeInSequence(temp, i, offset)
3.241 self.discard_value()
3.242
3.243 + def _storeInSequence(self, temp, i, offset=0):
3.244 +
3.245 + """
3.246 + Store the current active value in the fragment referenced by 'temp' at
3.247 + position 'i' with the given starting 'offset'.
3.248 + """
3.249 +
3.250 + self.new_op(temp)
3.251 + self.new_op(StoreAttr(Attr(i + offset, None, None)))
3.252 + self.set_source()
3.253 +
3.254 def _generateTestBoolean(self, node, temp):
3.255
3.256 """