You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by cd...@apache.org on 2014/07/22 15:36:06 UTC
[40/51] [partial] - Migrated the directory structure to a more Maven
style structure. - Started migrating the Parser from Antlr2+3 and JFlex to
Antlr4.
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/abc/optimize/PeepholeOptimizerMethodBodyVisitor.java
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/abc/optimize/PeepholeOptimizerMethodBodyVisitor.java b/compiler/src/main/java/org/apache/flex/abc/optimize/PeepholeOptimizerMethodBodyVisitor.java
new file mode 100644
index 0000000..1b03a94
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/abc/optimize/PeepholeOptimizerMethodBodyVisitor.java
@@ -0,0 +1,1520 @@
+/*
+ *
+ * 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.optimize;
+
+import static org.apache.flex.abc.ABCConstants.*;
+
+import org.apache.flex.abc.instructionlist.InstructionList;
+import org.apache.flex.abc.semantics.ECMASupport;
+import org.apache.flex.abc.semantics.Instruction;
+import org.apache.flex.abc.semantics.InstructionFactory;
+import org.apache.flex.abc.semantics.Label;
+import org.apache.flex.abc.visitors.DelegatingMethodBodyVisitor;
+import org.apache.flex.abc.visitors.IMethodBodyVisitor;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * IMethodBodyVisitor that will do peephole optimization of the method body.
+ * TODO: Not copmlete yet - should be equivalent to the peephole optimizer in
+ * MXMLC 4.5 Optimizations implemented: Eliminate unnecessary convert_d
+ * instructions Eliminate unnecessary convert_b instructions Eliminate
+ * unnecessary convert_i instructions Eliminate unnecessary convert_s
+ * instructions Eliminate unnecessary convert_u instructions Replace callprops
+ * where the return value is unused, with callpropvoid
+ */
+public class PeepholeOptimizerMethodBodyVisitor extends DelegatingMethodBodyVisitor
+{
+ /**
+ * How many instructions we can look back on
+ */
+ private static final int PEEPHOLE_WINDOW_SIZE = 4;
+
+ /**
+ * Constant that means we don't have any labels in the peephole window
+ */
+ private static final int NO_LABEL = -1;
+
+ public PeepholeOptimizerMethodBodyVisitor(IMethodBodyVisitor delegate)
+ {
+ super(delegate);
+ }
+
+ /**
+ * Hold a small window of previous instructions so that the optimizer can
+ * rewind and rewrite small sections of ABC
+ */
+ private List<InstructionInfo> instructions = new LinkedList<InstructionInfo>();
+
+ /**
+ * The index of the instruction with the most recently seen label - we can't
+ * look back past a label.
+ */
+ private int lastLabelSeen = NO_LABEL;
+
+ /**
+ * If we delete instructions that have labeling actions associated with
+ * them, we have to apply those labeling instructions to whatever
+ * instruction ends up replacing the deleted instruction(s). This list holds
+ * those labels.
+ */
+ private List<Label> labelsFromDeletedInsns = null;
+
+ /**
+ * basically holds a function object that sends instructions on to the right
+ * place once the peephole optimizer is finished. This will either send the
+ * instructions on to the next IMethodBodyVisitor, or to a new
+ * InstructionList
+ */
+ private InstructionFinisher finisher = new DelegateFinisher();
+
+ /**
+ * Resets the entire method body at once. The Peephole optimizer will
+ * process the entire instruction list, performing it's optimizations,
+ * before passing the InstructionList down to the next IMethodBodyVisitor. So
+ * any MethodBodyVisitors that the optimizer delegates to will receive a new
+ * InstructionList, with optimizations, instead of the InstructionList
+ * passed in.
+ *
+ * @param new_list The new Instructions for the method.
+ */
+ @Override
+ public void visitInstructionList(InstructionList new_list)
+ {
+ // for the delegates that run after this IVisitor, they will be processing
+ // a new copy of the instruction list, which will have the optimized opcodes
+ InstructionList newInstructions = new InstructionList();
+ InstructionFinisher old = this.finisher;
+
+ // Create a new finisher, which will put the instruction into a new InstructionList
+ // instead of passing them on to the next delegate
+ this.finisher = new InstructionListFinisher(newInstructions);
+ for (Instruction inst : new_list.getInstructions())
+ {
+ visitInstruction(inst);
+ }
+ // Make sure we flush any remaining instructions to the IL
+ flush();
+
+ // delegate the call with the new instruction list
+ super.visitInstructionList(newInstructions);
+
+ // Reset the finisher to the delegating one
+ this.finisher = old;
+ }
+
+ @Override
+ public void visitInstruction(int opcode)
+ {
+ visitInstruction(InstructionFactory.getInstruction(opcode));
+
+ }
+
+ @Override
+ public void visitInstruction(int opcode, int immediate_operand)
+ {
+ visitInstruction(InstructionFactory.getInstruction(opcode, immediate_operand));
+ }
+
+ @Override
+ public void visitInstruction(int opcode, Object[] operands)
+ {
+ visitInstruction(InstructionFactory.getInstruction(opcode, operands));
+ }
+
+ @Override
+ public void visitInstruction(int opcode, Object single_operand)
+ {
+ visitInstruction(InstructionFactory.getInstruction(opcode, single_operand));
+ }
+
+ @Override
+ /**
+ * Add the instruction to our peephole window and performs any optimizations.
+ *
+ * The optimizations are performed on the instructions already in the window - we do them
+ * now, and not when they are added, because we need to know if any labels targeted the instructions
+ * and we don't know that when we first see the instruction (but we do when we see the next instruction)
+ * @param instruction the Instruction to add
+ */
+ public void visitInstruction(Instruction instruction)
+ {
+ // Optimize the instructions we already have
+ processPreviousInstructions();
+
+ // add the instruction, but don't do any optimizations yet
+ addInstruction(instruction);
+ }
+
+ /**
+ * Perform optimizations on the instructions currently held in the window.
+ * This is so we don't do the optimizations until we are sure a particular
+ * instruction was or was not the target of a jump. If it was a target, then
+ * we don't do the optimizations as we don't have enough information to
+ * determine all the ways control might flow to that instruction.
+ */
+ private void processPreviousInstructions()
+ {
+ InstructionInfo last = previous(0);
+ InstructionInfo secondLast = previous(1);
+
+ // Check if we have a labelCurrent on the last instruction, or a labelNext on the previous instruction
+ if (last.getLabelCurrents().isEmpty() && secondLast.getLabelNexts().isEmpty())
+ {
+ switch (last.getOpcode())
+ {
+ case OP_convert_b:
+ {
+ op_convert_b(last);
+ break;
+ }
+ case OP_convert_d:
+ {
+ op_convert_d(last);
+ break;
+ }
+ case OP_convert_i:
+ {
+ op_convert_i(last);
+ break;
+ }
+ case OP_convert_u:
+ {
+ op_convert_u(last);
+ break;
+ }
+ case OP_convert_s:
+ {
+ op_convert_s(last);
+ break;
+ }
+ case OP_getproperty:
+ {
+ op_getproperty(last);
+ break;
+ }
+ case OP_iffalse:
+ {
+ op_iffalse(last);
+ break;
+ }
+ case OP_iftrue:
+ {
+ op_iftrue(last);
+ break;
+ }
+ case OP_pop:
+ {
+ op_pop(last);
+ break;
+ }
+ case OP_nop:
+ {
+ delete(0);
+ break;
+ }
+ case OP_getlocal:
+ {
+ OP_getlocal(last);
+ break;
+ }
+ case OP_returnvoid:
+ {
+ op_returnvoid(last);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ public void visitEnd()
+ {
+ flush();
+ super.visitEnd();
+ }
+
+ /**
+ * Flush any remaining instructions
+ */
+ private void flush()
+ {
+ processPreviousInstructions();
+
+ // finish any remaining instructions
+ for (InstructionInfo info : instructions)
+ {
+ finishInstruction(info);
+ }
+
+ instructions.clear();
+ }
+
+ /**
+ * Bind a Label object (not to be confused with the AVM OP_label
+ * instruction) to the last-visited ABC instruction in this method.
+ *
+ * @pre visitInstruction() must have been called at least once, i.e., there
+ * must be a last-visited ABC instruction.
+ */
+ public void labelCurrent(Label l)
+ {
+ jumpOptimizations(l, LabelKind.LABEL_CURRENT);
+
+ int idx = getLastInstructionIndex();
+ InstructionInfo instructionInfo = instructions.get(idx);
+ lastLabelSeen = idx;
+ instructionInfo.addLabelCurrent(l);
+ }
+
+ /**
+ * Enum to pass around to indicate what kind of labeling operation we are
+ * performing
+ */
+ private static enum LabelKind
+ {
+ LABEL_CURRENT,
+ LABEL_NEXT
+ }
+
+ /**
+ * Helper method to perform optimizations around jumps when we get a
+ * labeling operation. Shared with labelNext and labelCurrent methods
+ *
+ * @param l the Label object passed into the label operation
+ * @param kind what kind of labeling operation are we doing (labelNext or
+ * labelCurrent)
+ */
+ private void jumpOptimizations(Label l, LabelKind kind)
+ {
+ // Need to start looking at different indexes for label current vs. label next
+ // this is because for a label next, the next instruction won't have come in yet.
+ int idx = kind == LabelKind.LABEL_CURRENT ? 1 : 0;
+
+ InstructionInfo prev = previous(idx);
+ switch (prev.getOpcode())
+ {
+ case OP_jump:
+ {
+ InstructionInfo prev2 = previous(idx + 1);
+ Instruction insn = prev2.getInstruction();
+ if (insn != null && insn.isBranch() && insn.getTarget() == l)
+ {
+ // If the previous instructions were an if that jumped here, and it
+ // only jumped over another jump, then we can invert the if instruction
+ // and save a jump
+ // iffalse L1, jump L2, L1 -> iftrue L2, L1
+ Instruction newIf = invertIf(prev2, prev);
+ if (newIf != null)
+ {
+ if (kind == LabelKind.LABEL_CURRENT)
+ {
+ // labelCurrent, so we need to preserve the last instruction
+ Instruction[] newInsns = {newIf, previous(0).getInstruction()};
+ replace(idx + 1, newInsns);
+ }
+ else
+ {
+ // labelNext so we can just delete the last instruction
+ replace(idx + 1, newIf);
+ }
+ }
+ }
+ // If the previous instruction was a jump, and it just jumped
+ // to the next instruction, then we can remove the jump and just fall
+ // through
+ // jump L1, L1 -> L1
+ else if (prev.getOperand(0) == l)
+ {
+ if (kind == LabelKind.LABEL_NEXT)
+ // can just delete the jump because we don't have the next instruction yet
+ delete(idx);
+ else
+ // replace the jump with its target
+ replace(idx, previous(0).getInstruction());
+ }
+ }
+ }
+ }
+
+ /**
+ * Helper method to invert if expr target jump other-target to if not expr
+ * other-target Used to optimize some common patterns with if's and jumps
+ */
+ private Instruction invertIf(InstructionInfo oldIf, InstructionInfo oldJump)
+ {
+ Instruction newIf = null;
+ switch (oldIf.getOpcode())
+ {
+ case OP_ifeq:
+ {
+ newIf = InstructionFactory.createModifiedInstruction(OP_ifne, oldJump.getInstruction());
+ break;
+ }
+ case OP_ifstricteq:
+ {
+ newIf = InstructionFactory.createModifiedInstruction(OP_ifstrictne, oldJump.getInstruction());
+ break;
+ }
+ case OP_ifstrictne:
+ {
+ newIf = InstructionFactory.createModifiedInstruction(OP_ifstricteq, oldJump.getInstruction());
+ break;
+ }
+ case OP_ifge:
+ {
+ newIf = InstructionFactory.createModifiedInstruction(OP_ifnge, oldJump.getInstruction());
+ break;
+ }
+ case OP_ifgt:
+ {
+ newIf = InstructionFactory.createModifiedInstruction(OP_ifngt, oldJump.getInstruction());
+ break;
+ }
+ case OP_iflt:
+ {
+ newIf = InstructionFactory.createModifiedInstruction(OP_ifnlt, oldJump.getInstruction());
+ break;
+ }
+ case OP_ifle:
+ {
+ newIf = InstructionFactory.createModifiedInstruction(OP_ifnle, oldJump.getInstruction());
+ break;
+ }
+ case OP_ifne:
+ {
+ newIf = InstructionFactory.createModifiedInstruction(OP_ifeq, oldJump.getInstruction());
+ break;
+ }
+ case OP_ifnge:
+ {
+ newIf = InstructionFactory.createModifiedInstruction(OP_ifge, oldJump.getInstruction());
+ break;
+ }
+ case OP_ifngt:
+ {
+ newIf = InstructionFactory.createModifiedInstruction(OP_ifgt, oldJump.getInstruction());
+ break;
+ }
+ case OP_ifnlt:
+ {
+ newIf = InstructionFactory.createModifiedInstruction(OP_iflt, oldJump.getInstruction());
+ break;
+ }
+ case OP_ifnle:
+ {
+ newIf = InstructionFactory.createModifiedInstruction(OP_ifle, oldJump.getInstruction());
+ break;
+ }
+ case OP_iftrue:
+ {
+ newIf = InstructionFactory.createModifiedInstruction(OP_iffalse, oldJump.getInstruction());
+ break;
+ }
+ case OP_iffalse:
+ {
+ newIf = InstructionFactory.createModifiedInstruction(OP_iftrue, oldJump.getInstruction());
+ break;
+ }
+ }
+
+ return newIf;
+ }
+
+ /**
+ * Helper method to get the index of the last instruction in the window
+ *
+ * @return the index of the last instruction
+ */
+ private int getLastInstructionIndex()
+ {
+ return instructions.size() - 1;
+ }
+
+ /**
+ * Bind a Label object (not to be confused with the AVM OP_label
+ * instruction) to the next ABC instruction that gets visited in this method
+ *
+ * @post visitInstruction() must be called at least once after labelNext,
+ * i.e., there must be a next instruction.
+ */
+ public void labelNext(Label l)
+ {
+ jumpOptimizations(l, LabelKind.LABEL_NEXT);
+
+ int idx = getLastInstructionIndex();
+ instructions.get(idx).addLabelNext(l);
+ lastLabelSeen = idx + 1;
+ }
+
+ /**
+ * Fetch a previous instruction from the peephole window
+ *
+ * @param i the index of the previous instruction. 0 is the last instruction
+ * processed (so the immediately preceeding instruction), and
+ * PEEPHOLE_WINDOW_SIZE-1 is the oldest instruction that can be fetched.
+ * @return the InstructionInfo for some previous instruction, or the
+ * noInstruction object if i falls outside of the peephole window
+ */
+ private InstructionInfo previous(int i)
+ {
+ int size = instructions.size();
+ int idx = i + 1;
+ if (size >= idx)
+ {
+ int realIdx = size - idx;
+
+ // Can't look back past a label
+ if (indexBeforeLabel(realIdx))
+ return noInstruction;
+
+ return instructions.get(realIdx);
+ }
+ return noInstruction;
+ }
+
+ /**
+ * Helper method to determine if the index passed in occurrs before the last
+ * label instruction in the window
+ */
+ private boolean indexBeforeLabel(int idx)
+ {
+ return lastLabelSeen != NO_LABEL
+ // if we did a label next we can still look back until that
+ // next instruction comes in
+ && lastLabelSeen < PEEPHOLE_WINDOW_SIZE
+ && idx < lastLabelSeen;
+ }
+
+ /**
+ * Replace the previous instruction(s), from the end of the peephole window
+ * up to i with a new Instruction. It will replace all the previous
+ * instructions through i. Must not be called with an index that falls
+ * outside of the peephole window.
+ *
+ * @param i the index of the previous instruction to replace. Follows the
+ * same indexing strategy as the previous() method 0 is the last instruction
+ * in the window, 1 is two instructions ago, up to PEEPHOLE_WINDOW_SIZE-1
+ * which is the oldest instruction that can be replaced.
+ * @param insn The new instruction to insert at i
+ */
+ private void replace(int i, Instruction insn)
+ {
+ Instruction[] newInsns = {insn};
+ replace(i, newInsns);
+ }
+
+ /**
+ * Replace the previous instruction(s), from the end of the peephole window
+ * up to i with a new Instruction sequence. It will replace all the previous
+ * instructions through i. Must not be called with an index that falls
+ * outside of the peephole window.
+ *
+ * @param i the index of the previous instruction to replace. Follows the
+ * same indexing strategy as the previous() method 0 is the last instruction
+ * in the window, 1 is two instructions ago, up to PEEPHOLE_WINDOW_SIZE-1
+ * which is the oldest instruction that can be replaced.
+ * @param insns The new instruction sequence to insert at i
+ */
+ private void replace(int i, Instruction[] insns)
+ {
+ if (insns.length == 0)
+ return;
+
+ int size = instructions.size();
+ int idx = i + 1;
+ if (size >= idx)
+ {
+ int realIdx = size - idx;
+
+ assert !indexBeforeLabel(realIdx) : "Attempting to replace instruction sequence that spans a label";
+
+ // replace the instruction
+ InstructionInfo info = instructions.get(realIdx);
+ info.setInstruction(insns[0]);
+
+ // Delete any remaining instructions, saving the list of labelNexts from the last deleted
+ // instruction
+ List<Label> labelNexts = null;
+ for (int r = size - 1; r >= (realIdx + 1); --r)
+ {
+ InstructionInfo temp = instructions.remove(r);
+ if (r == size - 1)
+ {
+ // Save the labelNexts from the last deleted instruction
+ labelNexts = temp.getLabelNexts();
+ }
+ }
+
+ // Add the remaining instructions
+ for (int r = 1, l = insns.length; r < l; ++r)
+ addInstruction(insns[r]);
+
+ if (labelNexts != null)
+ {
+ // Apply any saved labelNexts to the last instruction added
+ for (Label l : labelNexts)
+ labelNext(l);
+ }
+ }
+ else
+ {
+ assert false : "Trying to replace instructions that fall outside the peephole window";
+ }
+ }
+
+ /**
+ * Delete the previous instruction(s), from the end of the peephole window
+ * up to i. Must not be called with an index that falls outside of the
+ * peephole window.
+ *
+ * @param i the index of the previous instruction to replace. Follows the
+ * same indexing strategy as the previous() method 0 is the last instruction
+ * in the window, 1 is two instructions ago, up to PEEPHOLE_WINDOW_SIZE-1
+ * which is the oldest instruction that can be replaced.
+ */
+ private void delete(int i)
+ {
+ int size = instructions.size();
+ int idx = i + 1;
+ if (size >= idx)
+ {
+ int realIdx = size - idx;
+
+ assert !indexBeforeLabel(realIdx) : "Attempting to delete instruction sequence that spans a label";
+
+ List<Label> labels = null;
+
+ for (int r = size - 1; r >= realIdx; --r)
+ {
+ InstructionInfo temp = instructions.remove(r);
+
+ if (r == size - 1)
+ {
+ // Save any label nexts from the last deleted instruction
+ List<Label> labelNexts = temp.getLabelNexts();
+ if (!labelNexts.isEmpty())
+ {
+ if (labels == null)
+ labels = new ArrayList<Label>();
+ labels.addAll(labelNexts);
+ }
+ }
+ else if (r == realIdx)
+ {
+ // save any label currents from the first deleted instruction
+ List<Label> labelCurrents = temp.getLabelCurrents();
+ if (!labelCurrents.isEmpty())
+ {
+ if (labels == null)
+ labels = new ArrayList<Label>();
+ labels.addAll(labelCurrents);
+ }
+ }
+ }
+ if (labels != null)
+ {
+ if (labelsFromDeletedInsns == null)
+ labelsFromDeletedInsns = labels;
+ else
+ labelsFromDeletedInsns.addAll(labels);
+ }
+ }
+ else
+ {
+ assert false : "Trying to delete instructions that fall outside the peephole window";
+ }
+ }
+
+ /**
+ * InstructionInfo to represent noInstruction - used when we look back past
+ * the size of the peephole window
+ */
+ private static InstructionInfo noInstruction = new InstructionInfo();
+
+ /**
+ * Optimizations for OP_convert_b: equals, convert_b -> equals strictequals,
+ * convert_b -> strictequals not, convert_b -> not greaterthan, convert_b ->
+ * greaterthan lessthan, convert_b -> lessthan greaterequals, convert_b ->
+ * greaterequals lessequals, convert_b -> lessequals istype, convert_b ->
+ * istype istypelate, convert_b -> istypelate instanceof, convert_b ->
+ * instanceof deleteproperty, convert_d -> deleteproperty in, convert_d ->
+ * in convert_b, convert_b -> convert_b pushtrue, convert_b -> pushtrue
+ * pushfalse, convert_b -> pushfalse
+ *
+ * @param i The convert_b instruction
+ */
+ private void op_convert_b(InstructionInfo i)
+ {
+ InstructionInfo prev = previous(1);
+ switch (prev.getOpcode())
+ {
+ case OP_equals:
+ case OP_strictequals:
+ case OP_not:
+ case OP_greaterthan:
+ case OP_lessthan:
+ case OP_greaterequals:
+ case OP_lessequals:
+ case OP_istype:
+ case OP_istypelate:
+ case OP_instanceof:
+ case OP_deleteproperty:
+ case OP_in:
+ case OP_convert_b:
+ case OP_pushtrue:
+ case OP_pushfalse:
+ {
+ // result is already a boolean, just erase the op_convert_b
+ delete(0);
+ break;
+ }
+ default:
+ {
+ // nothing to do - instruction has already been added
+ }
+ }
+ }
+
+ /**
+ * Optimizations for OP_convert_d: convert_d, convert_d -> convert_d
+ * pushbyte n, convert_d -> pushdouble n pushint n, convert_d -> pushdouble
+ * n pushuint n, convert_d -> pushdouble n pushdouble n, convert_d ->
+ * pushdouble n pushnan, convert_d -> pushnan lf32, convert_d -> lf32 lf64,
+ * convert_d -> lf64
+ *
+ * @param i The convert_d instruction
+ */
+ private void op_convert_d(InstructionInfo i)
+ {
+ InstructionInfo prev = previous(1);
+ switch (prev.getOpcode())
+ {
+ case OP_pushbyte:
+ {
+ // replace pushbyte, convert d with pushdouble - should be faster
+ replace(1, InstructionFactory.getInstruction(OP_pushdouble, new Double(convertByteImmediateToDouble(prev.getImmediate()))));
+ break;
+ }
+ case OP_pushint:
+ case OP_pushuint:
+ {
+ // replace pushint , convert d with pushdouble - should be faster
+ replace(1, InstructionFactory.getInstruction(OP_pushdouble, new Double(((Number)prev.getOperand(0)).doubleValue())));
+ break;
+ }
+ case OP_pushdouble:
+ case OP_pushnan:
+ case OP_lf32:
+ case OP_lf64:
+ case OP_convert_d:
+ {
+ // result is already a double, just erase the op_convert_d
+ delete(0);
+ break;
+ }
+ default:
+ {
+ // nothing to do - instruction has already been added
+ }
+ }
+ }
+
+ /**
+ * Optimizations for OP_convert_i: convert_i, convert_i -> convert_i
+ * coerce_i, convert_i -> coerce_i OP_bitand, convert_i -> OP_bitand
+ * OP_bitor, convert_i -> OP_bitor OP_bitxor, convert_i -> OP_bitxor lshift,
+ * convert_i -> lshift rshift, convert_i -> rshift add_i, convert_i -> add_i
+ * subtract_i, convert_i -> subtract_i increment_i, convert_i -> increment_i
+ * decrement_i, convert_i -> decrement_i multiply_i, convert_i -> multiply_i
+ * pushbyte, convert_i -> pushbyte pushshort, convert_i -> pushshort
+ * pushint, convert_i -> pushint li8, convert_i -> li8 li16, convert_i ->
+ * li16 li32, convert_i -> li32 sxi1, convert_i -> sxi1 sxi8, convert_i ->
+ * sxi8 sxi16, convert_i -> sxi16
+ *
+ * @param i The convert_b instruction
+ */
+ private void op_convert_i(InstructionInfo i)
+ {
+ InstructionInfo prev = previous(1);
+ switch (prev.getOpcode())
+ {
+ case OP_convert_i:
+ case OP_coerce_i:
+ case OP_bitand:
+ case OP_bitor:
+ case OP_bitxor:
+ case OP_lshift:
+ case OP_rshift:
+ case OP_add_i:
+ case OP_subtract_i:
+ case OP_increment_i:
+ case OP_decrement_i:
+ case OP_multiply_i:
+ case OP_pushbyte:
+ case OP_pushshort:
+ case OP_pushint:
+ case OP_li8:
+ case OP_li16:
+ case OP_li32:
+ case OP_sxi1:
+ case OP_sxi8:
+ case OP_sxi16:
+ {
+ // result is already an int, just erase the op_convert_i
+ delete(0);
+ break;
+ }
+ default:
+ {
+ // nothing to do - instruction has already been added
+ }
+ }
+ }
+
+ /**
+ * Optimizations for OP_convert_s: coerce_s, convert_s -> coerce_s
+ * convert_s, convert_s -> convert_s pushstring, convert_s -> pushstring
+ * typeof, convert_s -> typeof
+ *
+ * @param i The convert_b instruction
+ */
+ private void op_convert_s(InstructionInfo i)
+ {
+ InstructionInfo prev = previous(1);
+ switch (prev.getOpcode())
+ {
+ case OP_coerce_s:
+ case OP_convert_s:
+ case OP_pushstring:
+ case OP_typeof:
+ {
+ // result is already a String, just erase the op_convert_s
+ delete(0);
+ break;
+ }
+ default:
+ {
+ // nothing to do - instruction has already been added
+ }
+ }
+ }
+
+ /**
+ * Optimizations for OP_convert_i: convert_u, convert_u -> convert_u
+ * pushuint, convert_u -> pushuint
+ *
+ * @param i The convert_b instruction
+ */
+ private void op_convert_u(InstructionInfo i)
+ {
+ InstructionInfo prev = previous(1);
+ switch (prev.getOpcode())
+ {
+ case OP_convert_u:
+ case OP_pushuint:
+ {
+ // result is already a uint, just erase the op_convert_u
+ delete(0);
+ break;
+ }
+ default:
+ {
+ // nothing to do - instruction has already been added
+ }
+ }
+ }
+
+ /**
+ * Optimizations for OP_getproperty: findpropstrict name, getproperty name
+ * -> getlex name
+ *
+ * @param i the getproperty instruction
+ */
+ private void op_getproperty(InstructionInfo i)
+ {
+ InstructionInfo prev = previous(1);
+ switch (prev.getOpcode())
+ {
+ case OP_findpropstrict:
+ {
+ // can optimize findpropstrict, followed by getprop of the same name
+ // if there are any instructions btwn the findpropstrict and getprop (such as to compute a runtime
+ // multiname), we won't match this pattern
+ if (i.getOperand(0).equals(prev.getOperand(0)))
+ replace(1, InstructionFactory.createModifiedInstruction(OP_getlex, prev.getInstruction()));
+ break;
+ }
+ }
+ }
+
+ /**
+ * Optimizations for OP_iffalse: convert_b, iffalse -> iffalse equals,
+ * iffalse -> ifne strictequals, iffalse -> strictne lessthen, iffalse ->
+ * ifnlt lessequals, iffalse -> ifnle greaterthan, iffalse -> ifngt
+ * greaterequals, iffalse -> ifnge pushfalse, iffalse -> jump pushtrue,
+ * iffalse -> nothing strictequals, not, iffalse -> ifstrictequals equals,
+ * not, iffalse -> ifeq lessthan, not, iffalse -> iflt lessequals, not,
+ * iffalse -> ifle greaterthan, not, iffalse -> ifgt greaterequals,
+ * not,iffalse -> ifge not, iffalse -> iftrue
+ *
+ * @param i the iffalse instruction
+ */
+ private void op_iffalse(InstructionInfo i)
+ {
+ InstructionInfo prev = previous(1);
+
+ // Check if we know what's on the stack. If so, we don't need
+ // to do a conditional branch
+ switch(isTrueInstructionInfo(prev))
+ {
+ case TRUE:
+ delete(1); // if we can't branch, just eat the push / iffalse instructions
+ return;
+ case FALSE:
+ replace(1, InstructionFactory.createModifiedInstruction(OP_jump, i.getInstruction()));
+ // If we always branch, then replace the push / iffalse with an uncontidional jump
+ return;
+ case DONT_KNOW:
+ break; // continue on if we don't know it's a constant boolean
+ default:
+ assert false;
+
+ }
+ switch (prev.getOpcode())
+ {
+ case OP_convert_b:
+ {
+ // replace with just iffalse
+ replace(1, i.getInstruction());
+ break;
+ }
+ case OP_equals:
+ {
+ replace(1, InstructionFactory.createModifiedInstruction(OP_ifne, i.getInstruction()));
+ break;
+ }
+ case OP_strictequals:
+ {
+ replace(1, InstructionFactory.createModifiedInstruction(OP_ifstrictne, i.getInstruction()));
+ break;
+ }
+ case OP_lessthan:
+ {
+ replace(1, InstructionFactory.createModifiedInstruction(OP_ifnlt, i.getInstruction()));
+ break;
+ }
+ case OP_lessequals:
+ {
+ replace(1, InstructionFactory.createModifiedInstruction(OP_ifnle, i.getInstruction()));
+ break;
+ }
+ case OP_greaterthan:
+ {
+ replace(1, InstructionFactory.createModifiedInstruction(OP_ifngt, i.getInstruction()));
+ break;
+ }
+ case OP_greaterequals:
+ {
+ replace(1, InstructionFactory.createModifiedInstruction(OP_ifnge, i.getInstruction()));
+ break;
+ }
+
+ case OP_not:
+ {
+ InstructionInfo prev2 = previous(2);
+ switch (prev2.getOpcode())
+ {
+ case OP_strictequals:
+ {
+ replace(2, InstructionFactory.createModifiedInstruction(OP_ifstricteq, i.getInstruction()));
+ break;
+ }
+ case OP_equals:
+ {
+ replace(2, InstructionFactory.createModifiedInstruction(OP_ifeq, i.getInstruction()));
+ break;
+ }
+ case OP_lessthan:
+ {
+ replace(2, InstructionFactory.createModifiedInstruction(OP_iflt, i.getInstruction()));
+ break;
+ }
+ case OP_lessequals:
+ {
+ replace(2, InstructionFactory.createModifiedInstruction(OP_ifle, i.getInstruction()));
+ break;
+ }
+ case OP_greaterthan:
+ {
+ replace(2, InstructionFactory.createModifiedInstruction(OP_ifgt, i.getInstruction()));
+ break;
+ }
+ case OP_greaterequals:
+ {
+ replace(2, InstructionFactory.createModifiedInstruction(OP_ifge, i.getInstruction()));
+ break;
+ }
+ default:
+ {
+ replace(1, InstructionFactory.createModifiedInstruction(OP_iftrue, i.getInstruction()));
+ break;
+ }
+ }
+ break;
+ }
+
+ default:
+ {
+ // nothing to do, instruction was already added
+ }
+ }
+ }
+
+
+ /**
+ * Optimizations for OP_iftrue: convert_b, iftrue -> iftrue equals, iftrue
+ * -> ifeq strictequals, iftrue -> ifstricteq lessthen, iftrue -> iflt
+ * lessequals, iftrue -> ifle greaterthan, iftrue -> ifgt greaterequals,
+ * iftrue -> ifge pushfalse, iftrue -> jump pushtrue, iftrue -> nothing
+ * strictequals, not, iftrue -> ifstrictne equals, not, iftrue -> ifne
+ * lessthan, not, iftrue -> ifnlt lessequals, not, iftrue -> ifnle
+ * greaterthan, not, iftrue -> ifngt greaterequals, not,iftrue -> ifnge not,
+ * iftrue -> iffalse
+ *
+ * @param i the iftrue instruction
+ */
+ private void op_iftrue(InstructionInfo i)
+ {
+ InstructionInfo prev = previous(1);
+
+
+ switch(isTrueInstructionInfo(prev))
+ {
+ case FALSE:
+ delete(1);
+ return;
+ case TRUE:
+ replace(1, InstructionFactory.createModifiedInstruction(OP_jump, i.getInstruction()));
+ return;
+ case DONT_KNOW:
+ break; // continue on if we don't know it's a constant boolean
+ default:
+ assert false;
+
+ }
+ switch (prev.getOpcode())
+ {
+ case OP_convert_b:
+ {
+ replace(1, i.getInstruction());
+ break;
+ }
+ case OP_equals:
+ {
+ replace(1, InstructionFactory.createModifiedInstruction(OP_ifeq, i.getInstruction()));
+ break;
+ }
+ case OP_strictequals:
+ {
+ replace(1, InstructionFactory.createModifiedInstruction(OP_ifstricteq, i.getInstruction()));
+ break;
+ }
+ case OP_lessthan:
+ {
+ replace(1, InstructionFactory.createModifiedInstruction(OP_iflt, i.getInstruction()));
+ break;
+ }
+ case OP_lessequals:
+ {
+ replace(1, InstructionFactory.createModifiedInstruction(OP_ifle, i.getInstruction()));
+ break;
+ }
+ case OP_greaterthan:
+ {
+ replace(1, InstructionFactory.createModifiedInstruction(OP_ifgt, i.getInstruction()));
+ break;
+ }
+ case OP_greaterequals:
+ {
+ replace(1, InstructionFactory.createModifiedInstruction(OP_ifge, i.getInstruction()));
+ break;
+ }
+
+ case OP_not:
+ {
+ InstructionInfo prev2 = previous(2);
+ switch (prev2.getOpcode())
+ {
+ case OP_strictequals:
+ {
+ replace(2, InstructionFactory.createModifiedInstruction(OP_ifstrictne, i.getInstruction()));
+ break;
+ }
+ case OP_equals:
+ {
+ replace(2, InstructionFactory.createModifiedInstruction(OP_ifne, i.getInstruction()));
+ break;
+ }
+ case OP_lessthan:
+ {
+ replace(2, InstructionFactory.createModifiedInstruction(OP_ifnlt, i.getInstruction()));
+ break;
+ }
+ case OP_lessequals:
+ {
+ replace(2, InstructionFactory.createModifiedInstruction(OP_ifnle, i.getInstruction()));
+ break;
+ }
+ case OP_greaterthan:
+ {
+ replace(2, InstructionFactory.createModifiedInstruction(OP_ifngt, i.getInstruction()));
+ break;
+ }
+ case OP_greaterequals:
+ {
+ replace(2, InstructionFactory.createModifiedInstruction(OP_ifnge, i.getInstruction()));
+ break;
+ }
+ default:
+ {
+ replace(1, InstructionFactory.createModifiedInstruction(OP_iffalse, i.getInstruction()));
+ break;
+ }
+ }
+ break;
+ }
+
+ default:
+ // nothing to do, instruction was already added
+ }
+ }
+
+ private enum ConstantBoolean { TRUE, FALSE, DONT_KNOW }
+ /**
+ * evaluates an instruction and determines if it will result in true or false on TOS.
+ *
+ * @param i is an instruction to analyze
+ * @return whether TOS is true, false, or not known
+ */
+ private static ConstantBoolean isTrueInstructionInfo(InstructionInfo i)
+ {
+ ConstantBoolean ret = ConstantBoolean.DONT_KNOW;
+ switch(i.getOpcode())
+ {
+ case OP_pushtrue:
+ ret = ConstantBoolean.TRUE;
+ break;
+ case OP_pushfalse:
+ ret = ConstantBoolean.FALSE;
+ break;
+ case OP_pushbyte:
+ {
+ int value = i.getImmediate();
+ assert value >= 0;
+ ret = ECMASupport.toBoolean(value) ? ConstantBoolean.TRUE : ConstantBoolean.FALSE;
+ break;
+ }
+ case OP_pushint:
+ {
+ int value = (Integer)i.getOperand(0);
+ ret = ECMASupport.toBoolean(value) ? ConstantBoolean.TRUE : ConstantBoolean.FALSE;
+ break;
+ }
+ case OP_pushuint:
+ {
+ long value = (Long)i.getOperand(0);
+ ret = ECMASupport.toBoolean(value) ? ConstantBoolean.TRUE : ConstantBoolean.FALSE;
+ break;
+ }
+ case OP_pushstring:
+ {
+ String value = i.getOperand(0).toString();
+ ret = ECMASupport.toBoolean(value) ? ConstantBoolean.TRUE : ConstantBoolean.FALSE;
+ break;
+ }
+ case OP_pushnull:
+ ret = ConstantBoolean.FALSE;
+ break;
+ }
+ return ret;
+ }
+
+
+ /**
+ * Optimizations for OP_pop: callprop, pop -> callpropvoid callsuper, pop ->
+ * callsupervoid
+ */
+ private void op_pop(InstructionInfo i)
+ {
+ InstructionInfo prev = previous(1);
+ switch (prev.getOpcode())
+ {
+ case OP_callproperty:
+ {
+ replace(1, InstructionFactory.createModifiedInstruction(OP_callpropvoid, prev.getInstruction()));
+ break;
+ }
+ case OP_callsuper:
+ {
+ replace(1, InstructionFactory.createModifiedInstruction(OP_callsupervoid, prev.getInstruction()));
+ break;
+ }
+ default:
+ {
+ // nothing to do, instruction has already been added
+ }
+ }
+ }
+
+ /**
+ * Optimizations for OP_returnvoid: returnvoid, returnvoid -> returnvoid
+ * getlocal0, pushscope, returnvoid -> returnvoid (insn without side
+ * effect), returnvoid -> returnvoid
+ */
+ private void op_returnvoid(InstructionInfo i)
+ {
+ InstructionInfo prev = previous(1);
+ switch (prev.getOpcode())
+ {
+ case OP_returnvoid:
+ {
+ delete(0);
+ break;
+ }
+ // Any instruction without side effects that
+ // the CG is likely to generate can go here.
+ case OP_pop:
+ {
+ replace(1, i.getInstruction());
+ break;
+ }
+ case OP_pushscope:
+ {
+ if (previous(2).getOpcode() == OP_getlocal0)
+ // Trivial function.
+ replace(2, i.getInstruction());
+ break;
+ }
+ default:
+ {
+ // nothing to do, instruction has already been added
+ }
+ }
+ }
+
+ /**
+ * Optimizations for OP_getlocal: setlocal N, getlocal N -> dup, setlocal N
+ */
+ private void OP_getlocal(InstructionInfo i)
+ {
+ InstructionInfo prev = previous(1);
+ InstructionInfo cur = previous(0);
+
+ switch (prev.getOpcode())
+ {
+ case OP_setlocal:
+ {
+ if (cur.getInstruction().getImmediate() != prev.getInstruction().getImmediate())
+ break; // set,get of different locals
+
+ Instruction[] newInsns = {InstructionFactory.getInstruction(OP_dup), prev.getInstruction()};
+ replace(1, newInsns);
+ break;
+ }
+ default:
+ {
+ // nothing to do, instruction has already been added
+ }
+ }
+ }
+
+ /**
+ * convert the immediate of a pushbyte to a double value.
+ */
+ double convertByteImmediateToDouble(int i)
+ {
+ byte b = (byte)i;
+ return (double)b;
+ }
+
+ /**
+ * Add an instruction to the peephole window. If the window is already full
+ * then the first instruction will be removed, and passed on to the next
+ * delegate to make room for the new instruction
+ *
+ * @param insn the Instruction to add
+ */
+ private void addInstruction(Instruction insn)
+ {
+ int size = instructions.size();
+ InstructionInfo info = null;
+ if (size == PEEPHOLE_WINDOW_SIZE)
+ {
+ // reuse the InstructionInfo if we can
+ info = instructions.remove(0);
+ finishInstruction(info);
+
+ if (lastLabelSeen != NO_LABEL)
+ --lastLabelSeen;
+ }
+ else
+ {
+ info = new InstructionInfo();
+ }
+ info.reset(insn);
+ instructions.add(info);
+
+ if (labelsFromDeletedInsns != null)
+ {
+ // If we saved any labels from a deleted instruction sequence,
+ // apply them here.
+ // They will always be label currents no matter what the started out
+ // as - if we deleted something with a labelNext, this is still the next instruction
+ // and if we deleted something with a labelCurrent then this instruction is the new current instruction
+ for (Label l : labelsFromDeletedInsns)
+ labelCurrent(l);
+ labelsFromDeletedInsns = null;
+ }
+ }
+
+ /**
+ * Called when the peephole optimizer is finished with an instruction. This
+ * method will pass the instruction through to the next IMethodBodyVisitor,
+ * and call any needed labeling methods.
+ *
+ * @param info The InstructionInfo that the optimizer is done with
+ */
+ private void finishInstruction(InstructionInfo info)
+ {
+ finisher.visitInstruction(info.getInstruction());
+
+ for (Label l : info.getLabelCurrents())
+ {
+ finisher.labelCurrent(l);
+ }
+
+ for (Label l : info.getLabelNexts())
+ {
+ finisher.labelNext(l);
+ }
+ }
+
+ /**
+ * Helper class to hold info about an Instruction. In addition to the
+ * Instruction, it also keeps track of any labelNext, or labelCurrent
+ * instructions that must be applied to the Instruction once it is passed on
+ * to the next IMethodBodyVisitor
+ */
+ private static class InstructionInfo
+ {
+ InstructionInfo()
+ {
+ }
+
+ /**
+ * Hold the instruction
+ */
+ private Instruction insn;
+
+ /**
+ * label(s) to use for a labelNext operation once we are done with the
+ * instruction
+ */
+ private List<Label> labelNexts = new ArrayList<Label>();
+
+ /**
+ * label(s) to use for a labelCurrent operation once we are done with
+ * the instruction
+ */
+ private List<Label> labelCurrents = new ArrayList<Label>();
+
+ /**
+ * Reset the Instruction info to its default state with a new
+ * Instruction. This method will clear any labelNext/labelCurrent data.
+ * This is so we can reuse the InstructionInfo instances.
+ */
+ void reset(Instruction i)
+ {
+ this.insn = i;
+ labelNexts.clear();
+ labelCurrents.clear();
+ }
+
+ /**
+ * Change the Instruction this InstructionInfo points at. This is useful
+ * for rewriting instructions - instead of deleting and adding a new
+ * InstructionInfo, we can just rest the Instruction. This will not
+ * clear any labelNext/labelCurrent data - If we are replacing an
+ * Instruction then the label methods should still happen on the new
+ * instruction that replaced the old one.
+ */
+ void setInstruction(Instruction i)
+ {
+ this.insn = i;
+ }
+
+ /**
+ * @return the Instruction this instance holds
+ */
+ public Instruction getInstruction()
+ {
+ return insn;
+ }
+
+ /**
+ * Add a Label to be used as an argument for labelCurrent when this
+ * instruction is passed on to the next visitor.
+ *
+ * @param l the label
+ */
+ public void addLabelCurrent(Label l)
+ {
+ this.labelCurrents.add(l);
+ }
+
+ /**
+ * Add a Label to be used as an argument for labelNext when this
+ * instruction is passed on to the next visitor.
+ *
+ * @param l the label
+ */
+ public void addLabelNext(Label l)
+ {
+ this.labelNexts.add(l);
+ }
+
+ /**
+ * @return A List of Labels to be used for labelCurrent, returns the
+ * empty list if there are none
+ */
+ public List<Label> getLabelCurrents()
+ {
+ return labelCurrents;
+ }
+
+ /**
+ * @return A List of Labels to be used for labelNext, returns the empty
+ * list if there are none
+ */
+ public List<Label> getLabelNexts()
+ {
+ return labelNexts;
+ }
+
+ /**
+ * @return the opcode of the Instruction, or -1 if there is no
+ * instruction
+ */
+ public int getOpcode()
+ {
+ return insn != null ? insn.getOpcode() : -1;
+ }
+
+ /**
+ * @return the immediate for the instruction, or -1 if there is no
+ * instruction
+ */
+ public int getImmediate()
+ {
+ return insn != null ? insn.getImmediate() : -1;
+ }
+
+ /**
+ * Get the operand at index i from the underlying instruction
+ *
+ * @param i the index of the operand to fetch
+ * @return the operand at index i, or null if there is no instruction
+ */
+ public Object getOperand(int i)
+ {
+ return insn != null ? insn.getOperand(i) : null;
+ }
+ }
+
+ /**
+ * Interface so we can do different things when we are done with an
+ * instruction
+ */
+ static interface InstructionFinisher
+ {
+ void visitInstruction(Instruction i);
+
+ void labelNext(Label l);
+
+ void labelCurrent(Label l);
+ }
+
+ /**
+ * Passes the instruction and any label calls on to the next
+ * IMethodBodyVisitor
+ */
+ class DelegateFinisher implements InstructionFinisher
+ {
+ public void visitInstruction(Instruction i)
+ {
+ PeepholeOptimizerMethodBodyVisitor.super.visitInstruction(i);
+ }
+
+ public void labelNext(Label l)
+ {
+ PeepholeOptimizerMethodBodyVisitor.super.labelNext(l);
+ }
+
+ public void labelCurrent(Label l)
+ {
+ PeepholeOptimizerMethodBodyVisitor.super.labelCurrent(l);
+ }
+ }
+
+ /**
+ * Puts the instructions into a InstructionList instead of passing them on
+ * to the next IMethodBodyVisitor
+ */
+ static class InstructionListFinisher implements InstructionFinisher
+ {
+ public InstructionListFinisher(InstructionList list)
+ {
+ this.list = list;
+ }
+
+ private InstructionList list;
+
+ public void visitInstruction(Instruction i)
+ {
+ list.addInstruction(i);
+ }
+
+ public void labelNext(Label l)
+ {
+ list.labelNext(l);
+ }
+
+ public void labelCurrent(Label l)
+ {
+ list.labelCurrent(l);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/abc/optimize/package.html
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/abc/optimize/package.html b/compiler/src/main/java/org/apache/flex/abc/optimize/package.html
new file mode 100644
index 0000000..d52c83c
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/abc/optimize/package.html
@@ -0,0 +1,36 @@
+<!--
+
+ 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.
+
+-->
+
+<html>
+<body>
+
+This package contains specialized visitors that transform their input
+according to some figure of merit.
+
+<p>
+Typical figures of merit are:
+<ul>
+<li>Code size and estimated cost to execute instructions<li>
+<li>Graph normalization to exclude unreachable regions
+(dead code) and to remove unnecessary branch instructions.</li>
+</ul>
+</p>
+
+</body>
+</html>
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/abc/package.html
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/abc/package.html b/compiler/src/main/java/org/apache/flex/abc/package.html
new file mode 100644
index 0000000..3ef148d
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/abc/package.html
@@ -0,0 +1,41 @@
+<!--
+
+ 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.
+
+-->
+
+<html>
+<body>
+
+This package and its subpackages contain a support library
+for manipulating ActionScript bytecode (ABC).
+
+<p>
+ABC is the bytecode executed by the ActionScript Virtual Machine
+in Flash Player.
+The format and semantics of ABC is described in the document
+<a href="http://learn.adobe.com/wiki/display/AVM2/ActionScript+Virtual+Machine+2"><i>ActionScript Virtual Machine 2 (AVM2) Overview</i></a>.
+This library uses a set of visitor-pattern interfaces to
+expose the structure of ABC.
+</p>
+
+<p>
+The immediate contents of this package are classes for reading and writing ABC,
+such as <code>ABCParser</code> and <codde>ABCEmitter</code>.
+</p>
+
+</body>
+</html>