# HG changeset patch # User Paul Boddie # Date 1309567293 -7200 # Node ID b547786f38c7332e28c5b41465853c0a49b41888 # Parent c57594e433383bddc6b284d9b1d1c2a0337d2d0f Moved some code generation methods into a new Assembler class. Separated sequence element storage into a separate method which may form the basis of a native library routine. diff -r c57594e43338 -r b547786f38c7 micropython/ast.py --- a/micropython/ast.py Sun Jun 26 23:54:37 2011 +0200 +++ b/micropython/ast.py Sat Jul 02 02:41:33 2011 +0200 @@ -19,18 +19,18 @@ this program. If not, see . """ -from micropython.opt import Optimiser from micropython.common import * from micropython.data import * from micropython.rsvp import * from micropython.trans import Helper +from micropython.code import Assembler import compiler.ast # Program visitors. -class Translation(ASTVisitor, Helper): +class Translation(ASTVisitor, Assembler, Helper): - "A translated module." + "A module translator." # Attribute access instructions, for use with the appropriate handlers. @@ -53,6 +53,7 @@ """ ASTVisitor.__init__(self) + Assembler.__init__(self, program.optimisations) self.visitor = self self.module = module @@ -64,54 +65,18 @@ self.importer = self.program.get_importer() self.builtins = self.importer.modules.get("__builtins__") - # Optimisation. - - self.optimiser = Optimiser(self, program.optimisations) - - # The current unit being translated. - - self.unit = None + # Status flags. - # The temporary storage used by the current assignment expression. - - self.expr_temp = [] - - # Wiring within the code. - - self.labels = {} - self.label_number = 0 - self.loop_blocks = [] - self.exception_blocks = [] self.in_exception_handler = 0 self.in_assignment = 0 # for slicing and subscript + # Reset the assembler. + self.reset() def __repr__(self): return "Translation(%r)" % self.module - def reset(self): - - "Reset the state of the translator." - - # The code itself. This is limited to the code for a particular block - # being processed. - - self.blocks = [] - - # Information about temporary values. - - self.temp_positions = set() - self.max_temp_position = -1 - - # Information about instructions which construct frames. - - self.frame_makers = [] - - # Optimiser state must be reset for each unit. - - self.optimiser.reset() - def get_module_code(self): """ diff -r c57594e43338 -r b547786f38c7 micropython/code.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/micropython/code.py Sat Jul 02 02:41:33 2011 +0200 @@ -0,0 +1,298 @@ +#!/usr/bin/env python + +""" +Generate low-level code. + +Copyright (C) 2007, 2008, 2009, 2010, 2011 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +from micropython.opt import Optimiser +from micropython.common import * +from micropython.data import * +from micropython.rsvp import * +import compiler.ast + +class Assembler: + + "Support for generating low-level code." + + def __init__(self, optimisations): + + "Initialise the assembler with an optimiser and status attributes." + + # Optimisation. + + self.optimiser = Optimiser(self, optimisations) + + # The current unit being translated. + + self.unit = None + + # The temporary storage used by the current assignment expression. + + self.expr_temp = [] + + # Wiring within the code. + + self.labels = {} + self.label_number = 0 + self.loop_blocks = [] + self.exception_blocks = [] + + def reset(self): + + "Reset the state of the assembler." + + # The code itself. This is limited to the code for a particular block + # being processed. + + self.blocks = [] + + # Information about temporary values. + + self.temp_positions = set() + self.max_temp_position = -1 + + # Information about instructions which construct frames. + + self.frame_makers = [] + + # Optimiser state must be reset for each unit. + + self.optimiser.reset() + + def new_block(self): + + "Return a new code block." + + return Block() + + def set_block(self, block): + + "Add the given 'block' to the unit's list of blocks." + + self.optimiser.reset() + self.blocks.append(block) + + def get_loop_blocks(self): + return self.loop_blocks[-1] + + def add_loop_blocks(self, next_block, exit_block): + self.loop_blocks.append((next_block, exit_block)) + + def drop_loop_blocks(self): + self.loop_blocks.pop() + + def add_exception_unit(self): + self.exception_blocks.append([]) + + def get_exception_blocks(self): + return self.exception_blocks[-1][-1] + + def add_exception_blocks(self, handler_block, exit_block): + self.exception_blocks[-1].append((handler_block, exit_block)) + + def drop_exception_blocks(self): + self.exception_blocks[-1].pop() + + def drop_exception_unit(self): + self.exception_blocks.pop() + + # Assignment expression values. + + def record_value(self, immediate=1): + + """ + Record the current active value for an assignment. If the optional + 'immediate' parameter if set to a false value always allocates new + temporary storage to hold the recorded value; otherwise, the + value-providing instruction may be replicated in order to provide the + active value later on. + """ + + if immediate: + temp = self.optimiser.optimise_temp_storage() + else: + temp = self.get_temp() + self.expr_temp.append(temp) + + def discard_value(self): + + "Discard any temporary storage in use for the current assignment value." + + self.discard_temp(self.expr_temp.pop()) + + def set_source(self): + + """ + Set the source of an assignment using the current assignment value. This + sets the source input for the current instruction. + """ + + self.optimiser.set_source(self.expr_temp[-1]) + + # Optimise away constant storage if appropriate. + + if self.optimiser.optimise_constant_storage(): + self.remove_op() + + def is_immediate_user(self, node): + + """ + Return whether 'node' is an immediate user of an assignment expression. + """ + + return isinstance(node, (compiler.ast.AssName, compiler.ast.AssAttr)) + + def has_immediate_usage(self, nodes): + + """ + Return whether 'nodes' are all immediate users of an assignment expression. + """ + + for n in nodes: + if not self.is_immediate_user(n): + return 0 + return 1 + + # Temporary storage administration. + + def get_temp(self): + + """ + Add a temporary storage instruction for the current value and return a + sequence of access instructions. + """ + + position_in_frame = self.reserve_temp() + self.new_op(StoreTemp(position_in_frame)) + return LoadTemp(position_in_frame) + + def reserve_temp(self, temp_position=None): + + """ + Reserve a new temporary storage position, or if the optional + 'temp_position' is specified, ensure that this particular position is + reserved. + """ + + if temp_position is not None: + pass + elif not self.temp_positions: + temp_position = 0 + else: + temp_position = max(self.temp_positions) + 1 + + self.temp_positions.add(temp_position) + self.max_temp_position = max(self.max_temp_position, temp_position) + return self.unit.all_local_usage + temp_position # position in frame + + def ensure_temp(self, instruction=None): + + """ + Ensure that the 'instruction' is using a reserved temporary storage + position. + """ + + if isinstance(instruction, LoadTemp): + temp_position = instruction.attr - self.unit.all_local_usage + self.reserve_temp(temp_position) + + def discard_temp(self, instruction=None): + + "Discard any temporary storage position used by 'instruction'." + + if isinstance(instruction, LoadTemp): + temp_position = instruction.attr - self.unit.all_local_usage + self.free_temp(temp_position) + + def free_temp(self, temp_position): + + "Free the temporary storage position specified by 'temp_position'." + + if temp_position in self.temp_positions: + self.temp_positions.remove(temp_position) + + def set_frame_usage(self, node, extend): + + """ + Ensure that the frame usage for the unit associated with 'node' is set + on the 'extend' instruction. + """ + + # Remove any ExtendFrame instructions which do nothing. + + if self.last_op() is extend: + self.remove_op() + return + + ntemp = self.max_temp_position + 1 + extend.attr = ntemp + node.unit.local_usage # NOTE: See get_code for similar code. + + # Code writing methods. + + def new_op(self, op): + + """ + Add 'op' to the generated code, returning a true value if an instruction + was added. + """ + + # Optimise load operations employed by this instruction. + + self.optimiser.optimise_load_operations(op) + if self.optimiser.optimise_away_no_operations(op) or self.optimiser.optimise_unused_handlers(op): + return 0 + + # Add the operation to the current block. + + self.blocks[-1].code.append(op) + self.optimiser.set_new(op) + return 1 + + def remove_op(self): + + "Remove the last instruction." + + op = self.blocks[-1].code.pop() + self.optimiser.clear_active() + + def replace_op(self, op): + + "Replace the last added instruction with 'op'." + + self.remove_op() + self.new_op(op) + + def replace_active_value(self, op): + + """ + Replace the value-providing active instruction with 'op' if appropriate. + """ + + self.optimiser.remove_active_value() + self.new_op(op) + + def last_op(self): + + "Return the last added instruction." + + try: + return self.blocks[-1].code[-1] + except IndexError: + return None + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r c57594e43338 -r b547786f38c7 micropython/trans.py --- a/micropython/trans.py Sun Jun 26 23:54:37 2011 +0200 +++ b/micropython/trans.py Sat Jul 02 02:41:33 2011 +0200 @@ -89,229 +89,6 @@ else: raise TranslateError("No __builtins__ module is available for name %r." % name) - # Code feature methods. - - def new_block(self): - - "Return a new code block." - - return Block() - - def set_block(self, block): - - "Add the given 'block' to the unit's list of blocks." - - self.optimiser.reset() - self.blocks.append(block) - - def get_loop_blocks(self): - return self.loop_blocks[-1] - - def add_loop_blocks(self, next_block, exit_block): - self.loop_blocks.append((next_block, exit_block)) - - def drop_loop_blocks(self): - self.loop_blocks.pop() - - def add_exception_unit(self): - self.exception_blocks.append([]) - - def get_exception_blocks(self): - return self.exception_blocks[-1][-1] - - def add_exception_blocks(self, handler_block, exit_block): - self.exception_blocks[-1].append((handler_block, exit_block)) - - def drop_exception_blocks(self): - self.exception_blocks[-1].pop() - - def drop_exception_unit(self): - self.exception_blocks.pop() - - # Assignment expression values. - - def record_value(self, immediate=1): - - """ - Record the current active value for an assignment. If the optional - 'immediate' parameter if set to a false value always allocates new - temporary storage to hold the recorded value; otherwise, the - value-providing instruction may be replicated in order to provide the - active value later on. - """ - - if immediate: - temp = self.optimiser.optimise_temp_storage() - else: - temp = self.get_temp() - self.expr_temp.append(temp) - - def discard_value(self): - - "Discard any temporary storage in use for the current assignment value." - - self.discard_temp(self.expr_temp.pop()) - - def set_source(self): - - """ - Set the source of an assignment using the current assignment value. This - sets the source input for the current instruction. - """ - - self.optimiser.set_source(self.expr_temp[-1]) - - # Optimise away constant storage if appropriate. - - if self.optimiser.optimise_constant_storage(): - self.remove_op() - - def is_immediate_user(self, node): - - """ - Return whether 'node' is an immediate user of an assignment expression. - """ - - return isinstance(node, (compiler.ast.AssName, compiler.ast.AssAttr)) - - def has_immediate_usage(self, nodes): - - """ - Return whether 'nodes' are all immediate users of an assignment expression. - """ - - for n in nodes: - if not self.is_immediate_user(n): - return 0 - return 1 - - # Temporary storage administration. - - def get_temp(self): - - """ - Add a temporary storage instruction for the current value and return a - sequence of access instructions. - """ - - position_in_frame = self.reserve_temp() - self.new_op(StoreTemp(position_in_frame)) - return LoadTemp(position_in_frame) - - def reserve_temp(self, temp_position=None): - - """ - Reserve a new temporary storage position, or if the optional - 'temp_position' is specified, ensure that this particular position is - reserved. - """ - - if temp_position is not None: - pass - elif not self.temp_positions: - temp_position = 0 - else: - temp_position = max(self.temp_positions) + 1 - - self.temp_positions.add(temp_position) - self.max_temp_position = max(self.max_temp_position, temp_position) - return self.unit.all_local_usage + temp_position # position in frame - - def ensure_temp(self, instruction=None): - - """ - Ensure that the 'instruction' is using a reserved temporary storage - position. - """ - - if isinstance(instruction, LoadTemp): - temp_position = instruction.attr - self.unit.all_local_usage - self.reserve_temp(temp_position) - - def discard_temp(self, instruction=None): - - "Discard any temporary storage position used by 'instruction'." - - if isinstance(instruction, LoadTemp): - temp_position = instruction.attr - self.unit.all_local_usage - self.free_temp(temp_position) - - def free_temp(self, temp_position): - - "Free the temporary storage position specified by 'temp_position'." - - if temp_position in self.temp_positions: - self.temp_positions.remove(temp_position) - - def set_frame_usage(self, node, extend): - - """ - Ensure that the frame usage for the unit associated with 'node' is set - on the 'extend' instruction. - """ - - # Remove any ExtendFrame instructions which do nothing. - - if self.last_op() is extend: - self.remove_op() - return - - ntemp = self.max_temp_position + 1 - extend.attr = ntemp + node.unit.local_usage # NOTE: See get_code for similar code. - - # Code writing methods. - - def new_op(self, op): - - """ - Add 'op' to the generated code, returning a true value if an instruction - was added. - """ - - # Optimise load operations employed by this instruction. - - self.optimiser.optimise_load_operations(op) - if self.optimiser.optimise_away_no_operations(op) or self.optimiser.optimise_unused_handlers(op): - return 0 - - # Add the operation to the current block. - - self.blocks[-1].code.append(op) - self.optimiser.set_new(op) - return 1 - - def remove_op(self): - - "Remove the last instruction." - - op = self.blocks[-1].code.pop() - self.optimiser.clear_active() - - def replace_op(self, op): - - "Replace the last added instruction with 'op'." - - self.remove_op() - self.new_op(op) - - def replace_active_value(self, op): - - """ - Replace the value-providing active instruction with 'op' if appropriate. - """ - - self.optimiser.remove_active_value() - self.new_op(op) - - def last_op(self): - - "Return the last added instruction." - - try: - return self.blocks[-1].code[-1] - except IndexError: - return None - # Common methods. def _generateGuards(self, node): @@ -1479,11 +1256,20 @@ for i, n in enumerate(node.nodes): self.dispatch(n) self.record_value() - self.new_op(temp) - self.new_op(StoreAttr(Attr(i + offset, None, None))) - self.set_source() + self._storeInSequence(temp, i, offset) self.discard_value() + def _storeInSequence(self, temp, i, offset=0): + + """ + Store the current active value in the fragment referenced by 'temp' at + position 'i' with the given starting 'offset'. + """ + + self.new_op(temp) + self.new_op(StoreAttr(Attr(i + offset, None, None))) + self.set_source() + def _generateTestBoolean(self, node, temp): """