/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.flex.abc.instructionlist;
import java.util.ArrayList;
import java.util.Collection;
import java.util.NoSuchElementException;
import org.apache.flex.abc.ABCConstants;
import org.apache.flex.abc.semantics.Instruction;
import org.apache.flex.abc.semantics.InstructionFactory;
import org.apache.flex.abc.semantics.Label;
import static org.apache.flex.abc.ABCConstants.*;
/**
* An InstructionList is a structure that holds a sequence of Instructions, and
* manages sets of labels, which act as relocatable address constants, to
* compose control-flow constructs from their constituent parts.
*/
public class InstructionList implements Cloneable
{
/**
* Manifest constant returned when a search for an executable instruction
* doesn't find one.
*/
private static final int NO_EXECUTABLE_INSTRUCTIONS = -1;
/**
* Default constructor.
*/
public InstructionList()
{
}
/**
* Construct an InstructionList capable of holding a specified number of
* instructions.
*
* @param capacity - the required capacity.
*/
public InstructionList(int capacity)
{
this();
leafInstructions = new ArrayList<Instruction>(capacity);
}
/**
* This InstructionList's storage configuration; which elements of the fixed
* or variable storage are active.
*/
private enum StorageState
{
Variable, Ins2, Ins1, Empty, Ins3
};
/**
* Storage for a variable number of instructions.
*/
private ArrayList<Instruction> leafInstructions = null;
/**
* Fixed storage, used when the InstructionList contains 1-3 instructions.
*/
private Instruction insn1 = null;
/**
* Fixed storage, used when the InstructionList contains 2-3 instructions.
*/
private Instruction insn2 = null;
/**
* Fixed storage, used when the InstructionList contains 3 instructions.
*/
private Instruction insn3 = null;
/**
* Labels resolved to known positions in this InstructionList.
*/
private ArrayList<Label> activeLabels = null;
/**
* Labels that reference "the next instruction past the current end of this
* InstructionList." These labels will be resolved when another instruction
* is added to this list, either by addInstruction() or by addAll() when the
* other list has at least one insn. If no such instruction ever arrives,
* this list's pending labels will be inherited by any list it's added to
* via addAll().
*/
private ArrayList<Label> pendingLabels = null;
/**
* An InstructionList is valid from its creation until it is the operand of
* an addAll() operation to another InstructionList. At that point, the
* InstructionList's data becomes out-of-date and it is invalid.
*/
private boolean isValid = true;
/**
* Copy operation, used by clone()
*/
private void copyInstructionList(InstructionList src)
{
src.checkValidity();
// updateInstructionLabel() will update the relevant active/pending label
// collection with any cloned labels when needed.
ArrayList<Label> remainingActiveLabels = null;
if (src.activeLabels != null)
{
activeLabels = new ArrayList<Label>(src.activeLabels.size());
remainingActiveLabels = new ArrayList<Label>(src.activeLabels);
}
ArrayList<Label> remainingPendingLabels = null;
if (src.pendingLabels != null)
{
pendingLabels = new ArrayList<Label>(src.pendingLabels.size());
remainingPendingLabels = new ArrayList<Label>(src.pendingLabels);
}
// Shallow-copy the instructions.
if (!src.isEmpty())
{
insn1 = updateInstructionLabel(src.insn1, remainingActiveLabels, remainingPendingLabels);
insn2 = updateInstructionLabel(src.insn2, remainingActiveLabels, remainingPendingLabels);
insn3 = updateInstructionLabel(src.insn3, remainingActiveLabels, remainingPendingLabels);
if (src.leafInstructions != null)
{
leafInstructions = new ArrayList<Instruction>(src.leafInstructions.size());
for (int i = 0; i < src.leafInstructions.size(); i++)
leafInstructions.add(updateInstructionLabel(src.leafInstructions.get(i), remainingActiveLabels, remainingPendingLabels));
}
}
// clone any remaining labels from the src InstructionList which aren't
// referenced by an Instruction
if (remainingActiveLabels != null)
{
for (Label label : remainingActiveLabels)
{
activeLabels.add((Label)label.clone());
}
}
if (remainingPendingLabels != null)
{
for (Label label : remainingPendingLabels)
{
pendingLabels.add((Label)label.clone());
}
}
}
/**
* @return the first Instruction in this list.
* @throws NoSuchElementException if there are no instructions.
*/
public Instruction firstElement()
{
checkValidity();
switch (getStorageState())
{
case Variable:
{
return leafInstructions.get(0);
}
case Ins1:
case Ins2:
case Ins3:
{
return insn1;
}
default:
{
throw new NoSuchElementException();
}
}
}
/**
* @return the last element found.
* @throws NoSuchElementException if there are no instructions.
*/
public Instruction lastElement()
{
checkValidity();
switch (getStorageState())
{
case Variable:
{
return leafInstructions.get(leafInstructions.size() - 1);
}
case Ins3:
{
return insn3;
}
case Ins2:
{
return insn2;
}
case Ins1:
{
return insn1;
}
default:
{
throw new NoSuchElementException();
}
}
}
/**
* @return true if no Instructions are found.
*/
public boolean isEmpty()
{
checkValidity();
return size() == 0;
}
/**
* Fetch an InstructionList to which instructions can be appended.
* Synthesizes one if not already present.
*
* @return said list.
*/
public ArrayList<Instruction> getInstructions()
{
checkValidity();
switch (getStorageState())
{
case Ins3:
{
leafInstructions = new ArrayList<Instruction>();
leafInstructions.add(insn1);
insn1 = null;
leafInstructions.add(insn2);
insn2 = null;
leafInstructions.add(insn3);
insn3 = null;
break;
}
case Ins2:
{
leafInstructions = new ArrayList<Instruction>();
leafInstructions.add(insn1);
insn1 = null;
leafInstructions.add(insn2);
insn2 = null;
break;
}
case Ins1:
{
leafInstructions = new ArrayList<Instruction>();
leafInstructions.add(insn1);
insn1 = null;
break;
}
case Empty:
{
leafInstructions = new ArrayList<Instruction>();
break;
}
case Variable:
{
// Nothing to do.
break;
}
default:
{
assert false : "Unknown storage state " + getStorageState();
}
}
return leafInstructions;
}
/**
* Fetch this InstructionList's label.
*
* @return a synthetic Label that identifies this InstructionList.
*/
public Label getLabel()
{
checkValidity();
Label result = new Label();
labelFirst(result);
return result;
}
/**
* Add an instruction to the sequence.
*
* @param insn the instruction to be added.
* @return the input insn.
*/
public Instruction addInstruction(Instruction insn)
{
checkValidity();
// If the incoming instruction is executable,
// then any pending labels can be resolved to
// its location.
if (insn.isExecutable())
resolvePendingLabels();
switch (getStorageState())
{
// Adding an instruction to a full fixed-storage
// configuration spills into variable-length storage.
case Ins3:
case Variable:
{
getInstructions().add(insn);
break;
}
case Empty:
{
insn1 = insn;
break;
}
case Ins1:
{
insn2 = insn;
break;
}
case Ins2:
{
insn3 = insn;
break;
}
default:
{
assert false : "Unknown storage state " + getStorageState();
}
}
return insn;
}
/**
* Convenience method adds an instruction with no operands.
*
* @param opcode - the instruction's opcode.
* @return the generated instruction.
*/
public Instruction addInstruction(int opcode)
{
return addInstruction(InstructionFactory.getInstruction(opcode));
}
/**
* Convenience method adds an instruction with an immediate operand.
*
* @param opcode - the instruction's opcode.
* @param immed - the immediate operand.
*/
public Instruction addInstruction(int opcode, int immed)
{
return addInstruction(InstructionFactory.getInstruction(opcode, immed));
}
/**
* Convenience method adds an instruction with operands.
*
* @param opcode - the instruction's opcode.
* @param operands - the operands.
*/
public Instruction addInstruction(int opcode, Object[] operands)
{
return addInstruction(InstructionFactory.getInstruction(opcode, operands));
}
/**
* Convenience method adds an instruction with a single operand.
*
* @param opcode - the instruction's opcode.
* @param operand - the operand.
*/
public Instruction addInstruction(int opcode, Object operand)
{
return addInstruction(InstructionFactory.getInstruction(opcode, operand));
}
/**
* Add another InstructionList to the sequence.
*
* @param src_list the InstructionList to be added.
* @post instructions and pending labels from src_list appended to this
* list.
* @post src_list is invalidated.
* @post if this list had any pending labels, and src_list contained at
* least one instruction, the pending labels from this list are resolved to
* the first instruction contributed by src_list.
*/
public void addAll(InstructionList src_list)
{
checkValidity();
src_list.checkValidity();
// Inherit active labels from the other list, and
// adjust their positions relative to this list.
if (src_list.activeLabels != null)
{
if (!isEmpty())
{
for (Label l : src_list.activeLabels)
{
l.adjustOffset(size());
}
}
getActiveLabels().addAll(src_list.activeLabels);
}
if (!src_list.isEmpty())
{
// If the new instruction sequence contains any executable instructions,
// resolve this list's pending labels to the position of the first new
// executable instruction in the merged list.
int firstExecutableOffset = src_list.firstExecutableOffset();
if (firstExecutableOffset != NO_EXECUTABLE_INSTRUCTIONS)
resolvePendingLabels(size() + firstExecutableOffset);
// This state machine copies src_list's fixed or variable
// storage into this list with as few calls to getInstructions()
// as feasible, since getInstructions() always converts this
// list to relatively expensive variable-length storage.
StorageState this_state = getStorageState();
StorageState src_state = src_list.getStorageState();
switch (this_state)
{
case Empty:
{
switch (src_state)
{
case Empty:
{
break;
}
case Ins1:
case Ins2:
case Ins3:
{
insn1 = src_list.insn1;
insn2 = src_list.insn2;
insn3 = src_list.insn3;
break;
}
case Variable:
{
// Note: shared src_list objects will
// clone their leafInstructions list.
leafInstructions = src_list.leafInstructions;
break;
}
default:
{
assert false : "Unknown storage state " + src_state;
}
}
break;
}
case Ins1:
{
switch (src_state)
{
case Empty:
{
break;
}
case Ins1:
case Ins2:
{
insn2 = src_list.insn1;
insn3 = src_list.insn2;
break;
}
case Ins3:
{
getInstructions().add(src_list.insn1);
getInstructions().add(src_list.insn2);
getInstructions().add(src_list.insn3);
break;
}
case Variable:
{
getInstructions().addAll(src_list.getInstructions());
break;
}
default:
{
assert (false) : "Unknown storage state " + src_state;
}
}
break;
}
case Ins2:
{
switch (src_state)
{
case Empty:
{
break;
}
case Ins1:
{
insn3 = src_list.insn1;
break;
}
case Ins2:
{
getInstructions().add(src_list.insn1);
getInstructions().add(src_list.insn2);
break;
}
case Ins3:
{
getInstructions().add(src_list.insn1);
getInstructions().add(src_list.insn2);
getInstructions().add(src_list.insn3);
break;
}
case Variable:
{
getInstructions().addAll(src_list.getInstructions());
break;
}
default:
{
assert (false) : "Unknown storage state " + src_state;
}
}
break;
}
case Ins3:
{
switch (src_state)
{
case Empty:
{
break;
}
case Ins1:
{
getInstructions().add(src_list.insn1);
break;
}
case Ins2:
{
getInstructions().add(src_list.insn1);
getInstructions().add(src_list.insn2);
break;
}
case Ins3:
{
getInstructions().add(src_list.insn1);
getInstructions().add(src_list.insn2);
getInstructions().add(src_list.insn3);
break;
}
case Variable:
{
getInstructions().addAll(src_list.getInstructions());
break;
}
default:
{
assert (false) : "Unknown storage state " + src_state;
}
}
break;
}
case Variable:
{
switch (src_state)
{
case Empty:
{
break;
}
case Ins1:
{
leafInstructions.add(src_list.insn1);
break;
}
case Ins2:
{
leafInstructions.add(src_list.insn1);
leafInstructions.add(src_list.insn2);
break;
}
case Ins3:
{
leafInstructions.add(src_list.insn1);
leafInstructions.add(src_list.insn2);
leafInstructions.add(src_list.insn3);
break;
}
case Variable:
{
leafInstructions.addAll(src_list.getInstructions());
break;
}
default:
{
assert (false) : "Unknown storage state " + src_state;
}
}
break;
}
default:
{
assert false : "Unknown storage state " + this_state;
}
}
}
// Inherit any pending labels from the other list.
if (src_list.pendingLabels != null)
{
if (pendingLabels == null)
pendingLabels = new ArrayList<Label>();
pendingLabels.addAll(src_list.pendingLabels);
}
// Invalidate the source list.
src_list.isValid = false;
}
/**
* Label the first executable instruction in this InstructionList.
*
* @param l - the Label to be associated with the first executable
* instruction in the list.
*/
public void labelFirst(Label l)
{
if (firstExecutableOffset() != NO_EXECUTABLE_INSTRUCTIONS)
addLabelAt(l, firstExecutableOffset());
else
labelNext(l);
}
/**
* Label the last executable instruction in this InstructionList.
*
* @param l - the Label to be associated with the last executable
* instruction in the list.
*/
public void labelCurrent(Label l)
{
if (l.targetMustBeExecutable() && lastExecutableOffset() != NO_EXECUTABLE_INSTRUCTIONS)
addLabelAt(l, lastExecutableOffset());
else if (!l.targetMustBeExecutable() && size() > 0)
addLabelAt(l, size() - 1);
else
labelNext(l);
}
/**
* Add a label at an arbitrary position.
*
* @param l - the label.
* @param pos - the label's position.
*/
public void addLabelAt(Label l, int pos)
{
checkValidity();
if (!isEmpty())
{
assert (pos < size());
if (l.getPosition() == Label.NO_POSITION)
l.setPosition(pos);
else
assert (l.getPosition() == pos) : "Label position " + l.getPosition() + " != " + pos;
addLabel(l);
}
else
{
assert (pos == 0);
labelNext(l);
}
}
private void addLabel(Label l)
{
getActiveLabels().add(l);
}
/**
* @return this InstructionList's active labels (i.e., labels resolved to a
* known offset within this InstructionList).
*/
public ArrayList<Label> getActiveLabels()
{
checkValidity();
if (null == activeLabels)
activeLabels = new ArrayList<Label>();
return activeLabels;
}
/**
* @return a Label bound to the last position in this InstructionList.
* @pre the list cannot be empty.
*/
public Label getLastLabel()
{
checkValidity();
Label result = new Label();
// The InstructionList is never empty
// when this routine is called.
labelCurrent(result);
return result;
}
/**
* Add a Label to this InstructionList's pendingLabels.
*
* @param l - the Label to be set pending.
*/
public void labelNext(Label l)
{
checkValidity();
if (null == pendingLabels)
pendingLabels = new ArrayList<Label>();
pendingLabels.add(l);
}
/**
* A suitable target instruction has presented itself; resolve all pending
* labels.
*/
private void resolvePendingLabels()
{
resolvePendingLabels(size());
}
private void resolvePendingLabels(int offset)
{
if (pendingLabels != null)
{
for (Label l : pendingLabels)
{
l.setPosition(offset);
}
// Move the pending labels to active status.
getActiveLabels().addAll(pendingLabels);
pendingLabels = null;
}
}
/**
* Search this InstructionList for the first executable instruction (i.e.,
* not a debugging instruction).
*
* @return the offset of this instruction, or NO_EXECUTABLE_INSTRUCTIONS
*/
private int firstExecutableOffset()
{
switch (getStorageState())
{
case Variable:
{
int size = leafInstructions.size();
for (int offset = 0; offset < size; offset++)
{
if (leafInstructions.get(offset).isExecutable())
return offset;
}
break;
}
case Ins3:
{
if (insn1.isExecutable())
return 0;
else if (insn2.isExecutable())
return 1;
else if (insn3.isExecutable())
return 2;
break;
}
case Ins2:
{
if (insn1.isExecutable())
return 0;
if (insn2.isExecutable())
return 1;
break;
}
case Ins1:
{
if (insn1.isExecutable())
return 0;
break;
}
default:
assert (false) : "Unknown storage state " + getStorageState();
// fall through
case Empty:
break;
}
return NO_EXECUTABLE_INSTRUCTIONS;
}
/**
* Search this InstructionList for the last executable instruction (i.e.,
* not a debugging instruction).
*
* @return the offset of this instruction, or NO_EXECUTABLE_INSTRUCTIONS
*/
private int lastExecutableOffset()
{
switch (getStorageState())
{
case Variable:
{
for (int offset = size() - 1; offset >= 0; offset--)
if (leafInstructions.get(offset).isExecutable())
return offset;
break;
}
case Ins3:
{
if (insn3.isExecutable())
return 2;
else if (insn2.isExecutable())
return 1;
else if (insn1.isExecutable())
return 0;
break;
}
case Ins2:
{
if (insn2.isExecutable())
return 1;
else if (insn1.isExecutable())
return 0;
break;
}
case Ins1:
{
if (insn1.isExecutable())
return 0;
break;
}
default:
assert (false) : "Unknown storage state " + getStorageState();
// fall through
case Empty:
break;
}
return NO_EXECUTABLE_INSTRUCTIONS;
}
/**
* @return the size of this InstructionList; zero if the list has no
* instructions, or the number of instructions.
*/
public int size()
{
// Note: Explicitly does not call checkValidity(),
// size() is called even after the list is invalidated.
switch (getStorageState())
{
case Variable:
return leafInstructions.size();
case Ins3:
return 3;
case Ins2:
return 2;
case Ins1:
return 1;
default:
assert false : "Unknown storage state " + getStorageState();
// fall through
case Empty:
return 0;
}
}
/**
* Query the storage configuration.
*
* @return one of the values of StorageState that describes the present
* storage configuration.
*/
private StorageState getStorageState()
{
if (leafInstructions != null)
{
return StorageState.Variable;
}
else if (insn3 != null)
{
assert (insn1 != null && insn2 != null);
return StorageState.Ins3;
}
else if (insn2 != null)
{
assert (insn1 != null);
return StorageState.Ins2;
}
else if (insn1 != null)
{
return StorageState.Ins1;
}
else
{
return StorageState.Empty;
}
}
/**
* @return true if this InstructionList has unresolved pending labels.
*/
public boolean hasPendingLabels()
{
checkValidity();
return pendingLabels != null && !pendingLabels.isEmpty();
}
/**
* This InstructionList is the "body" of a statement that needs to add more
* logic. Any pending labels from the body statement(s) need to be
* re-assigned to the owning InstructionList at a later time.
*
* @return this list's [former] pendingLabels.
* @post pendingLabels is null.
*/
public Collection<Label> stripPendingLabels()
{
checkValidity();
ArrayList<Label> pending_labels = pendingLabels;
pendingLabels = null;
return pending_labels;
}
/**
* Add pending labels acquired from a component list to this list's
* pendingLabels collection.
*
* @param prev_pending - pending labels returned by a call to
* stripPendingLabels().
*/
public void addAllPendingLabels(Collection<Label> prev_pending)
{
checkValidity();
if (prev_pending != null)
{
if (null == pendingLabels)
pendingLabels = new ArrayList<Label>();
pendingLabels.addAll(prev_pending);
}
}
/**
* @return true if the given InstructionList does not have an unconditional
* transfer of control as its last instruction.
*/
public boolean canFallThrough()
{
checkValidity();
boolean can_fall_through = true;
if (size() > 0)
{
// Look for an unconditional transfer of control.
int last_opcode = lastElement().getOpcode();
can_fall_through = ABCConstants.OP_returnvoid != last_opcode &&
ABCConstants.OP_returnvalue != last_opcode &&
ABCConstants.OP_jump != last_opcode &&
ABCConstants.OP_throw != last_opcode;
}
return can_fall_through;
}
/**
* Ensure an InstructionList is not used after it's been invalidated.
*/
private void checkValidity()
{
if (!isValid)
throw new IllegalStateException("Invalid InstructionList");
}
/**
* If the src Instruction contains a Label operand, return a new Instruction
* with a new cloned Label, otherwise the same Instruction will be returned.
* When a new Label is introduced, the relevant active or pending labels
* collection will also be updated.
*/
private Instruction updateInstructionLabel(Instruction src, Collection<Label> srcActiveLabels, Collection<Label> srcPendingLabels)
{
if (src == null)
return null;
// non-targetable instructions don't have labels
if (!src.isTargetableInstruction())
return src;
final int operandCount = src.getOperandCount();
if (operandCount == 0)
return src;
Object[] newOperands = new Object[operandCount];
for (int i = 0; i < operandCount; i++)
{
Object operand = src.getOperand(i);
if (!(operand instanceof Label))
{
newOperands[i] = operand;
continue;
}
Label srcLabel = (Label)operand;
Label clonedLabel = (Label)(srcLabel).clone();
newOperands[i] = clonedLabel;
// Remove the src Label from the active or pending set, and add
// in the new cloned Label into the set from whence in came.
if (srcActiveLabels != null && srcActiveLabels.remove(srcLabel))
activeLabels.add(clonedLabel);
else if (srcPendingLabels != null && srcPendingLabels.remove(srcLabel))
pendingLabels.add(clonedLabel);
}
if (operandCount == 1)
return InstructionFactory.getInstruction(src.getOpcode(), newOperands[0]);
else
return InstructionFactory.getInstruction(src.getOpcode(), newOperands);
}
/**
* Format the InstructionList for debugging purposes.
*/
@Override
public String toString()
{
StringBuffer result = new StringBuffer();
if (insn1 != null)
{
result.append(insn1.toString());
result.append('\n');
}
if (insn2 != null)
{
result.append(insn2.toString());
result.append('\n');
}
if (insn3 != null)
{
result.append(insn3.toString());
result.append('\n');
}
if (leafInstructions != null)
{
// TODO: List out the labels.
for (Instruction insn : leafInstructions)
{
result.append(insn.toString());
result.append('\n');
}
}
return result.toString();
}
/**
* Clone this InstructionList; make a shallow copy of the Instructions, deep
* copies of the labels.
*
* @return a cloned InstructionList.
*/
@Override
public Object clone()
{
InstructionList newb = null;
try
{
newb = (InstructionList)super.clone();
}
catch (Exception cantHappen)
{
// Compiler appeasement.
assert false : cantHappen;
}
newb.copyInstructionList(this);
return newb;
}
/**
* Search for an Instruction with a specific opcode.
*
* @param opcode - the opcode of interest.
* @return true if an instruction with this opcode is part of this
* InstructionList.
*/
public boolean hasSuchInstruction(int opcode)
{
return findOccurrences(opcode, true) > 0;
}
/**
* Count occurrences of an Instruction with a specific opcode.
*
* @param opcode - the opcode of interest.
* @return the count of occurrences.
*/
public int countOccurrences(int opcode)
{
return findOccurrences(opcode, false);
}
/**
* Find occurrences of an Instruction with a specific opcode.
*
* @param opcode - the opcode of interest.
* @param stop_after_first - return a nonzero count after finding at least
* one occurence (1-3 occurences in the fixed storage, the first occurence
* in variable storage).
* @return the count of occurrences, or 0/1/2/3 if stop_after_first is set.
*/
public int findOccurrences(int opcode, boolean stop_after_first)
{
int result = 0;
switch (getStorageState())
{
case Empty:
break;
case Ins3:
if (insn3.getOpcode() == opcode)
result++;
// fall through
case Ins2:
if (insn2.getOpcode() == opcode)
result++;
// fall through
case Ins1:
if (insn1.getOpcode() == opcode)
result++;
break;
case Variable:
for (Instruction insn : leafInstructions)
{
if (insn.getOpcode() == opcode)
{
result++;
if (stop_after_first)
break;
}
}
}
return result;
}
/**
* Add an Instruction to this InstructionList to push a numeric constant
* onto the value stack.
*
* @param value - the value to push.
*/
public void pushNumericConstant(final long value)
{
if (value >= -128 && value < 128)
addInstruction(OP_pushbyte, (int)(value));
else if (value > 0 && value < 32768)
addInstruction(OP_pushshort, (int)value);
else if (value > -0xFFFFFFFF && value < 0XFFFFFFFE)
addInstruction(OP_pushint, Integer.valueOf((int)value));
else
addInstruction(OP_pushdouble, Double.valueOf(value));
}
/**
* Interface used by code-gen clients that need a chance to adjust the
* instruction list after it is generated. Clients are discouraged from
* doing this, and use this interface at their own risk
*/
public interface IFilter
{
public InstructionList filter(InstructionList il);
}
}