http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/abc/models/TreeModelEncoder.java ---------------------------------------------------------------------- diff --git a/compiler/src/main/java/org/apache/flex/abc/models/TreeModelEncoder.java b/compiler/src/main/java/org/apache/flex/abc/models/TreeModelEncoder.java new file mode 100644 index 0000000..7146c22 --- /dev/null +++ b/compiler/src/main/java/org/apache/flex/abc/models/TreeModelEncoder.java @@ -0,0 +1,1101 @@ +/* + * + * 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.models; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.apache.flex.abc.semantics.ExceptionInfo; +import org.apache.flex.abc.semantics.Instruction; +import org.apache.flex.abc.semantics.MethodBodyInfo; +import org.apache.flex.abc.visitors.IDiagnosticsVisitor; +import org.apache.flex.abc.visitors.NilDiagnosticsVisitor; +import org.apache.flex.abc.graph.IFlowgraph; +import org.apache.flex.abc.graph.IBasicBlock; +import org.apache.flex.abc.graph.algorithms.DominatorTree.Multimap; + +/** + * The TreeModelEncoder translates the stack-oriented semantics + * of ABC bytecode into a tree-oriented model. + */ +public class TreeModelEncoder<T> +{ + /** + * Construct a new TreeModelEncoder. + * @param mbi - the method body of interest. + * @param visitor - the TreeModelVisitor that's interested. + * @param diagnosticsVisitor - a handler for any diagnostics generated. + */ + public TreeModelEncoder( MethodBodyInfo mbi, TreeModelVisitor<T> visitor, IDiagnosticsVisitor diagnosticsVisitor) + { + this.mbi = mbi; + this.visitor = visitor; + this.diagnosticsVisitor = diagnosticsVisitor; + + this.visitor.visit(this); + + setUpFrames(); + placePhiNodes(); + visitFrames(); + + this.visitor.visitEnd(); + } + + /** + * The Method of interest. + */ + private final MethodBodyInfo mbi; + + /** + * The model visitor we're driving. + */ + private final TreeModelVisitor<T> visitor; + + /** + * Receiver of any diagnostic output. + */ + private final IDiagnosticsVisitor diagnosticsVisitor; + + /** + * The Block currently under examination. + */ + IBasicBlock currentBlock; + + /** + * The FrameModelEncoder that's driving this TreeModelEncoder. + */ + private FrameModelEncoder encoder; + + /** + * The symbolic representations of this method's locals. + */ + ArrayList<Object> localSymbolicReferences = new ArrayList<Object>(); + + /** + * The symbolic representations of this method's value stack slots. + * Note: There is only one symbol for each element of the stack itself, + * not a symbol for each value the stack will hold over its lifetime. + */ + ArrayList<Object> valueSymbolicReferences = new ArrayList<Object>(); + + /** + * The symbolic representations of this method's scope stack slots. + * Note: There is only one symbol for each element of the stack itself, + * not a symbol for each scope the stack will hold over its lifetime. + */ + ArrayList<Object> scopeSymbolicReferences = new ArrayList<Object>(); + + /** + * Blocks modifying frame elements, keyed by + * the symbolic representation of the frame element. + */ + private Map<Object,Set<IBasicBlock>> a = new HashMap<Object,Set<IBasicBlock>>(); + + /** + * Get the MethodBodyInfo of the method being analyzed. + * @return the MethodBodyInfo being analyzed. + */ + public MethodBodyInfo getMethodBodyInfo() + { + return this.mbi; + } + + /** + * Get the method's flowgraph. + * @return the IFlowGraph of the method being analyzed. + */ + public IFlowgraph getCfg() + { + return this.mbi.getCfg(); + } + + /** + * Get the block currently being analyzed. + * @return the block currently being analyzed. + */ + public IBasicBlock getCurrentBlock() + { + return this.currentBlock; + } + + /** + * Get the index of the instruction currently being analyzed. + * @return the intra-Block index of the instruction currently being analyzed. + */ + public int getInstructionIndex() + { + return this.encoder.getInstructionIndex(); + } + + /** + * Do a preliminary pass over the method to set up + * the frames' extents and live-out sets. + */ + private void setUpFrames() + { + mbi.getCfg().traverseGraph( + new FrameModelEncoder( + this.mbi, + new FrameSetupVisitor(), + new NilDiagnosticsVisitor() + ) + ); + } + + /** + * Place phi-nodes at blocks' dominance frontiers + * to model dataflow merges in frame state. + */ + private void placePhiNodes() + { + int iterCount = 0; + + Multimap<IBasicBlock> df = getCfg().getDominatorTree().getDominanceFrontiers(); + + Map<IBasicBlock, Integer> hasAlready = new HashMap<IBasicBlock,Integer>(); + Map<IBasicBlock, Integer> work = new HashMap<IBasicBlock, Integer>(); + + for ( Object local: localSymbolicReferences ) + iterCount = placePhiNodes(iterCount, local, df, a, hasAlready, work); + for ( Object scope: scopeSymbolicReferences ) + iterCount = placePhiNodes(iterCount, scope, df, a, hasAlready, work); + for ( Object value: valueSymbolicReferences ) + iterCount = placePhiNodes(iterCount, value, df, a, hasAlready, work); + } + + /** + * Place phi-nodes for one particular element of the frame. + * @param initialIteration - the initial value of the iteration counter. + * @param v - the frame element being analyzed. + * @param df - the method's dominance frontiers. + * @param a - the map of Blocks that assign to various frame elements. + * @param hasAlready - a map of Block-to-iteration-count values, used + * to note a Block that already has a phi node for the element. + * @param work - a map of Block-to-iteration-count values, used + * to find Blocks that need further analysis. + */ + private int placePhiNodes( + final int initialIteration, + final Object v, + Multimap<IBasicBlock> df, + Map<Object,Set<IBasicBlock>> a, + Map<IBasicBlock,Integer> hasAlready, + Map<IBasicBlock,Integer> work + ) + { + if ( ! a.containsKey(v) ) + return initialIteration; + + int iterCount = initialIteration + 1; + + HashSet<IBasicBlock> w = new HashSet<IBasicBlock>(); + + for ( IBasicBlock x: a.get(v) ) + { + work.put(x, iterCount); + w.add(x); + } + + while ( ! w.isEmpty() ) + { + Iterator<IBasicBlock> it = w.iterator(); + IBasicBlock x = it.next(); + it.remove(); + + for ( IBasicBlock y: df.get(x) ) + { + if ( !hasAlready.containsKey(y) || hasAlready.get(y).intValue() < iterCount ) + { + placePhiNode(y,v); + hasAlready.put(y, iterCount); + + if ( !work.containsKey(y) || work.get(y).intValue() < iterCount ) + { + work.put(y, iterCount); + w.add(y); + } + } + } + } + + return iterCount; + } + + /** + * Place a phi-node for a particular (Block,frame element) tuple. + * @param target - the Block. + * @param frameKey - the variable. + */ + void placePhiNode(IBasicBlock target, Object frameKey) + { + Frame targetFrame = getFrame(target); + + // Figure out which value this is. + + int idx = this.localSymbolicReferences.indexOf(frameKey); + + if ( idx != -1 ) + { + if ( needsInitializer(targetFrame.locals, idx) ) + setFrameElement(targetFrame.locals, idx, visitor.addMergePoint(currentBlock)); + } + else + { + idx = this.valueSymbolicReferences.indexOf(frameKey); + + if ( idx != -1 ) + { + if ( needsInitializer(targetFrame.values, idx) ) + setFrameElement(targetFrame.values, idx, visitor.addMergePoint(currentBlock)); + } + else + { + idx = scopeSymbolicReferences.indexOf(frameKey); + assert idx != -1; + if ( needsInitializer(targetFrame.scopes, idx) ) + setFrameElement(targetFrame.scopes, idx, visitor.addMergePoint(currentBlock)); + } + } + } + + /** + * Determine if a particular frame element needs an initializer. + * @param elements - the frame elements (locals, scope stack, or value stack). + * @param idx - the index of the frame element of interest. + * @return true if the given index has no initializer object. + */ + private boolean needsInitializer(ArrayList<? extends Object> elements, int idx) + { + return ( elements.size() <= idx || elements.get(idx) == null ); + } + + /** + * Initialize a frame element with an anonymous marker object if necessary. + * @param elements - the frame elements (locals, scope stack, or value stack). + * @param idx - the index of the frame element of interest. + */ + private void touchFrameElement(ArrayList<Object> elements, int idx) + { + if ( needsInitializer(elements, idx) ) + setFrameElement(elements, idx, new Object()); + } + + /** + * Set a frame element. + * @param elements - the frame elements (locals, scope stack, or value stack). + * @param idx - the index of the frame element of interest. + * @param value - the value to set. + */ + private <E> void setFrameElement(ArrayList<E> elements, int idx, final E value) + { + while ( elements.size() <= idx ) + elements.add(null); + elements.set(idx, value); + } + + /** + * Modify a frame element; touch it and add the modifying Block to its + * set of assigning blocks. + * @param elements - the frame elements (locals, scope stack, or value stack). + * @param idx - the index of the frame element of interest. + * @param b - the Block that modifies the element. + */ + private void modifyFrameElement(ArrayList<Object> elements, int idx, IBasicBlock b) + { + touchFrameElement(elements,idx); + + if ( ! a.containsKey(elements.get(idx)) ) + a.put(elements.get(idx), new HashSet<IBasicBlock>()); + a.get(elements.get(idx)).add(b); + } + + /** + * Visit each block, its associated Frame, and the + * instructions in each block, showing each in turn + * to the TreeModelVisitor and recording its results + * in the Frame. + */ + private void visitFrames() + { + ArrayList<T> parameters = new ArrayList<T>(); + + // Load the initial frame's locals with parameter information. + for ( int i = 0; i < this.mbi.getMethodInfo().getParamCount(); i++ ) + parameters.add(visitor.translateParameter(i)); + + Frame startFrame = getFrame(this.mbi.getCfg().getStartBlock()); + startFrame.locals.addAll(parameters); + + // Initialize exception-handling targets with the exception variable, + // and set up a dataflow merge node for each local that may be read + // in the block. + // TODO: This encoder makes pessimistic assumptions about dataflow + // into the locals, i.e., it assumes every exception handler is + // globally reachable. + for ( ExceptionInfo ex: this.mbi.getExceptions() ) + { + IBasicBlock catchTarget = this.mbi.getCfg().getBlock(ex.getTarget()); + Frame catchFrame = getFrame(catchTarget); + + setFrameElement(catchFrame.values, 0, visitor.translateExceptionVariable(ex.getCatchVar(), ex.getExceptionType())); + + for ( int i = 0; i < parameters.size(); i++ ) + { + catchFrame.locals.add(visitor.addMergePoint(catchTarget)); + @SuppressWarnings("unchecked") + TreeModelVisitor.IMergePoint<T> mergeNode = (TreeModelVisitor.IMergePoint<T>)catchFrame.locals.get(i); + mergeNode.addValue(parameters.get(i)); + } + + // Initialize the other locals' merge nodes, which are initially empty. + for ( int i = parameters.size(); i < localSymbolicReferences.size(); i++ ) + { + catchFrame.locals.add(visitor.addMergePoint(catchTarget)); + } + } + + this.encoder = new FrameModelEncoder( this.mbi, new ModelDrivingVisitor(this.visitor), this.diagnosticsVisitor ); + this.mbi.getCfg().traverseGraph( this.encoder ); + } + + /** + * A representation of an AVM "frame," the local variables, scope stack slots, + * and value stack slots used in a particular Block of the method's flowgraph. + */ + public class Frame + { + /** + * The local variables used in the Block. + */ + public final ArrayList<T> locals = new ArrayList<T>(); + + /** + * The scope stack slots used in the Block. + */ + public final ArrayList<T> scopes = new ArrayList<T>(); + + /** + * The value stack slots used in the Block. + */ + public final ArrayList<T> values = new ArrayList<T>(); + + /** + * @return the value on top of the value stack. + */ + public T tos() + { + return this.values.get(valueStackDepth()); + } + + /** + * Remove a value from the value stack. + * Visitors must modify the Frame, but should + * maintain their own modifiable view of it. + */ + private T popValue() + { + return popElement(this.values); + } + + /** + * Push a value onto the value stack. + * Visitors must modify the Frame, but should + * maintain their own modifiable view of it. + */ + private T pushValue(T value) + { + this.values.add(value); + return value; + } + + /** + * Push a value onto the scope stack. + * Visitors must modify the Frame, but should + * maintain their own modifiable view of it. + */ + private T pushScope(T scope) + { + this.scopes.add(scope); + return scope; + } + + /** + * Pop a value off the scope stack. + * Visitors must modify the Frame, but should + * maintain their own modifiable view of it. + */ + private T popScope() + { + return popElement(this.scopes); + } + + /** + * Get a local variable. + * Visitors must modify the Frame, but should + * maintain their own modifiable view of it. + */ + private T getlocal(int idx) + { + adjustSize(this.locals, idx+1); + return this.locals.get(idx); + } + + /** + * Set a local variable. + * Visitors must modify the Frame, but should + * maintain their own modifiable view of it. + */ + private T setlocal(int idx, T value) + { + adjustSize(this.locals, idx+1); + this.locals.set(idx, value); + propagateLocalToCatchBlocks(idx,value); + + return value; + } + + /** + * Get a scope object. + * Visitors must modify the Frame, but should + * maintain their own modifiable view of it. + */ + private T getscopeobject(int idx) + { + adjustSize(this.scopes, idx+1); + return this.scopes.get(idx); + } + + /** + * Verify that the value stack contains + * at least the required number of live values. + * @param required - the numbe of values required. + * @return true if the value stack has the required number of values. + */ + private boolean verifyStackDepth(int required) + { + return this.values.size() >= required; + } + + /** + * @return the number of live values on the value stack. + */ + private int valueStackDepth() + { + return this.values.size() - 1; + } + + /** + * Verify that the scope stack contains + * at least the required number of live values. + * @param required - the numbe of values required. + * @return true if the scope stack has the required number of values. + */ + private boolean verifyScopeDepth(int required) + { + return this.scopes.size() >= required; + } + + /** + * @return the number of live values on the scope stack. + */ + @SuppressWarnings("unused") + private int scopeStackDepth() + { + return this.scopes.size() - 1; + } + } + + /** + * Propagate a local variable's value to all catch blocks. + * TODO: this should only propagate to catch blocks reachable + * from the current block; this code errs on the side of pessimism. + * @param idx the index of the local. + * @param value the value to propagate. + */ + void propagateLocalToCatchBlocks(int idx, T value) + { + for ( ExceptionInfo ex: mbi.getExceptions() ) + { + IBasicBlock catchTarget = mbi.getCfg().getBlock(ex.getTarget()); + Frame catchFrame = getFrame(catchTarget); + + if ( catchFrame.locals.get(idx) instanceof TreeModelVisitor.IMergePoint ) + { + @SuppressWarnings("unchecked") + TreeModelVisitor.IMergePoint<T> mergeNode = (TreeModelVisitor.IMergePoint<T>)catchFrame.locals.get(idx); + mergeNode.addValue(value); + } + // else it's an exception variable. + } + } + + /** + * Active Frames, keyed by their generating Block. + */ + private Map<IBasicBlock,Frame> framesByBlock = new HashMap<IBasicBlock,Frame>(); + + /** + * Get the Frame that corresponds to a Block. + * @param b - the Block of interest. + * @return the Frame mapped to the Block. + */ + public Frame getFrame(IBasicBlock b) + { + if ( ! this.framesByBlock.containsKey(b) ) + this.framesByBlock.put(b, new Frame()); + return this.framesByBlock.get(b); + } + + /** + * The FrameSetupVisitor creates Frame objects for the Blocks, + * and drives the modifyFrameElement calls that initialize + * the map of Blocks that assign values to specific frame elements. + */ + private class FrameSetupVisitor implements FrameModelVisitor<T> + { + IBasicBlock currentBlock = null; + BlockState blockState = null; + + public void visit(FrameModelEncoder encoder) + { + } + + public void visitEnd() + { + } + + public T noFrameEffect(Instruction i) + { + return null; + } + + public T consumeValue(Instruction i, int count) + { + // The model driving visitor detects stack underflow. + blockState.stackDepth = Math.max(blockState.stackDepth - count, 0); + return null; + } + + /** + * Handle an instruction that pushes a value onto the stack. + * @param i - the Instruction. + */ + public T produceValue(Instruction i) + { + modifyFrameElement(valueSymbolicReferences, blockState.stackDepth, currentBlock); + blockState.stackDepth++; + return null; + } + + public T consumeAndProduceValue(Instruction i, int consumeCount) + { + consumeValue(null, consumeCount); + produceValue(null); + return null; + } + + public T branch(Instruction i, IBasicBlock target) + { + return null; + } + + public T multiwayBranch(Instruction i, Collection<IBasicBlock> targets) + { + return null; + } + + public T getlocal(Instruction i, int idx) + { + touchFrameElement(localSymbolicReferences, idx); + return null; + } + + public T setlocal(Instruction i, int idx) + { + modifyFrameElement(localSymbolicReferences, idx, currentBlock); + return null; + } + + public void modifyLocal(Instruction i, int idx) + { + modifyFrameElement(localSymbolicReferences, idx, currentBlock); + } + + public T moveValueToScopeStack(Instruction i) + { + consumeValue(null, 1); + modifyFrameElement(scopeSymbolicReferences, blockState.scopeDepth, currentBlock); + blockState.scopeDepth++; + return null; + } + + public T popscope(Instruction i) + { + blockState.scopeDepth = Math.max(blockState.scopeDepth - 1, 0); + return null; + } + + public T getScopeobject(Instruction i, int idx) + { + touchFrameElement(scopeSymbolicReferences, idx); + return null; + } + + public T hasnext2(Instruction i) + { + modifyFrameElement(localSymbolicReferences, (Integer)i.getOperand(0), currentBlock); + modifyFrameElement(localSymbolicReferences, (Integer)i.getOperand(1), currentBlock); + return null; + } + + public T dup(Instruction i) + { + produceValue(null); + return null; + } + + public T swap(Instruction i) + { + // No effect on the frame setup. + return null; + } + + public boolean visitBlock(IBasicBlock b) + { + if ( visited.add(b) ) + { + assert this.currentBlock == null; + this.currentBlock = b; + this.blockState = getBlockState(b); + return true; + } + else + { + return false; + } + } + + /** + * Blocks visisted so far. + */ + private Set<IBasicBlock> visited = new HashSet<IBasicBlock>(); + + /** + * End the visit to a block; ensure that all its + * frame elements are in place. + */ + public void visitEndBlock(IBasicBlock b) + { + for ( int i = 0; i < this.blockState.stackDepth; i++ ) + touchFrameElement(valueSymbolicReferences, i); + + for ( int i = 0; i < this.blockState.scopeDepth; i++ ) + touchFrameElement(scopeSymbolicReferences, i); + + this.currentBlock = null; + this.blockState = null; + } + + /** + * Propagate value/scope stack depth information + * from one Block to its target block. + */ + public void visitEdge(IBasicBlock from, IBasicBlock target) + { + assert(from == this.currentBlock); + BlockState targetState = getBlockState(target); + + targetState.stackDepth = blockState.stackDepth; + targetState.scopeDepth = blockState.scopeDepth; + } + + + /** + * Get the scope/value stack depth tracker for a Block. + * @param b - the Block of interest. + * @return the BlockState tracker mapped to it. + */ + private BlockState getBlockState(IBasicBlock b) + { + if ( ! this.statesByBlock.containsKey(b) ) + this.statesByBlock.put(b, new BlockState()); + return this.statesByBlock.get(b); + } + + private Map<IBasicBlock, BlockState> statesByBlock = new HashMap<IBasicBlock,BlockState>(); + } + + private static class BlockState + { + int stackDepth = 0; + int scopeDepth = 0; + } + + /** + * The ModelDrivingVisitor makes a second pass over the method's + * control flow graph, after the Frames have been initialized and + * dataflow merge points placed, and drives the visitor's traversal + * of the method. + */ + private class ModelDrivingVisitor implements FrameModelVisitor<T> + { + ModelDrivingVisitor(TreeModelVisitor<T> visitor) + { + this.visitor = visitor; + } + + final TreeModelVisitor<T> visitor; + + public void visit(FrameModelEncoder encoder) + { + assert(encoder == TreeModelEncoder.this.encoder); + } + + public void visitEnd() + { + TreeModelEncoder.this.encoder = null; + } + + /** + * The Frame that corresponds to the block being visited. + */ + Frame currentFrame = null; + + @Override + public T noFrameEffect(Instruction i) + { + return visitor.translate(i, noOperands()); + } + + /** + * Handle an instruction that consumes value stack elements. + * @param i - the Instruction. + * @param count - the number of value stack elements consumed. + */ + public T consumeValue(Instruction i, int count) + { + if ( this.currentFrame.verifyStackDepth(count) ) + { + ArrayList<T> operands = new ArrayList<T>(count); + for ( int j = 0; j < count; j++ ) + operands.add(this.currentFrame.popValue()); + return visitor.translate(i, operands); + } + else + { + return visitor.valueStackUnderflow(i, count); + } + } + + /** + * Handle an instruction that pushes a value onto the stack. + * @param i - the Instruction. + */ + public T produceValue(Instruction i) + { + return this.currentFrame.pushValue(visitor.translate(i, noOperands())); + } + + /** + * Handle an instruction that consumes value stack elements, + * and then pushes a new value onto the stack. + * @param i - the Instruction. + * @param consumeCount - the number of value stack elements consumed. + */ + public T consumeAndProduceValue(Instruction i, int consumeCount) + { + if ( this.currentFrame.verifyStackDepth(consumeCount) ) + { + ArrayList<T> operands = new ArrayList<T>(consumeCount); + for ( int j = 0; j < consumeCount; j++ ) + operands.add(this.currentFrame.popValue()); + return this.currentFrame.pushValue(visitor.translate(i, operands)); + } + else + { + return visitor.valueStackUnderflow(i, consumeCount); + } + } + + /** + * Handle a branch instruction. + * @param i - the Instruction. + * @param target - the Instruction's target. Instructions with + * fall-through semantics also implicitly target the next Block. + */ + public T branch(Instruction i, IBasicBlock target) + { + return visitor.translateBranch(i, singleOperand(target)); + } + + /** + * Handle a multibranch instruction. + * @param i - the Instruction. + * @param targets - the Instruction's targets. + */ + public T multiwayBranch(Instruction i, Collection<IBasicBlock> targets) + { + return visitor.translateBranch(i, targets); + } + + /** + * Get a local variable, leaving its value on the stack. + * @param i - the Instruction. + * @param idx - the variable's index. + */ + public T getlocal(Instruction i, int idx) + { + adjustSize(currentFrame.locals, idx + 1); + T result = visitor.translate(i, singleOperand(this.currentFrame.getlocal(idx))); + this.currentFrame.pushValue(result); + return result; + } + + /** + * Set a local variable, comsuming a value from the stack. + * @param i - the Instruction. + * @param idx - the variable's index. + */ + public T setlocal(Instruction i, int idx) + { + adjustSize(currentFrame.locals, idx + 1); + if ( this.currentFrame.verifyStackDepth(1) ) + { + T result = visitor.translate(i, singleOperand(this.currentFrame.popValue())); + return this.currentFrame.setlocal(idx, result); + } + else + { + return this.currentFrame.setlocal(idx, visitor.valueStackUnderflow(i, 1)); + } + } + + /** + * Modify a local variable. + * @param i - the Instruction. + * @param idx - the variable's index. + */ + public void modifyLocal(Instruction i, int idx) + { + visitor.translate(i, noOperands()); + } + + /** + * Pop a value off the value stack and push it on the scope stack. + * @param i - the Instruction (an OP_pushscope). + */ + public T moveValueToScopeStack(Instruction i) + { + if ( this.currentFrame.verifyStackDepth(1) ) + return this.currentFrame.pushScope(visitor.translate(i, singleOperand(this.currentFrame.popValue()))); + else + return visitor.valueStackUnderflow(i, 1); + } + + /** + * Pop a value off the scope stack. + * @param i - the Instruction (an OP_popscope). + */ + public T popscope(Instruction i) + { + if ( this.currentFrame.verifyScopeDepth(1) ) + return visitor.translate(i, singleOperand(this.currentFrame.popScope())); + else + return visitor.scopeStackUnderflow(i, 1); + + } + + /** + * Get a particular scope stack element. + * @param i - the Instruction. + * @param idx - the index of the scope element. + */ + public T getScopeobject(Instruction i, int idx) + { + if ( this.currentFrame.verifyScopeDepth(idx+1) ) + return this.currentFrame.pushValue(visitor.translate(i, singleOperand(this.currentFrame.scopes.get(idx)))); + else + return visitor.scopeStackUnderflow(i, 1); + } + + /** + * Handle the special-case hasnext2 instruction. + * @param i - the Instruction. + */ + public T hasnext2(Instruction i) + { + return null; + } + + /** + * Handle the stack-maintenance dup instruction. + * @param i - the Instruction. + */ + public T dup(Instruction i) + { + if ( this.currentFrame.verifyStackDepth(1) ) + { + return this.currentFrame.pushValue(visitor.translate(i, singleOperand(this.currentFrame.tos()))); + } + else + { + return visitor.valueStackUnderflow(i, 1); + } + + } + + /** + * Handle the stack-maintenance swap instruction. + * @param i - the Instruction. + */ + public T swap(Instruction i) + { + if ( this.currentFrame.verifyStackDepth(2) ) + { + int stackDepth = this.currentFrame.valueStackDepth(); + T temp = visitor.translate(i, singleOperand(this.currentFrame.tos())); + this.currentFrame.values.set( stackDepth, this.currentFrame.values.get(stackDepth-1) ); + this.currentFrame.values.set( stackDepth - 1, temp); + return this.currentFrame.tos(); + } + else + { + return visitor.valueStackUnderflow(i, 2); + } + } + + @Override + public boolean visitBlock(IBasicBlock b) + { + Frame frame = getFrame(b); + currentBlock = b; + this.currentFrame = frame; + + if ( visitor.visitBlock(b) ) + { + return true; + } + else + { + // Tear down. + this.currentFrame = null; + currentBlock = null; + return false; + } + + } + + @Override + public void visitEndBlock(IBasicBlock b) + { + visitor.visitEnd(b); + this.currentFrame = null; + currentBlock = null; + } + + @Override + public void visitEdge(IBasicBlock from, IBasicBlock target) + { + assert getFrame(from) == this.currentFrame; + + Frame targetFrame = getFrame(target); + + for ( int i = 0; i < this.currentFrame.locals.size(); i++ ) + addInitializer(i, targetFrame.locals, this.currentFrame.getlocal(i)); + for ( int i = 0; i < this.currentFrame.values.size(); i++ ) + addInitializer(i, targetFrame.values, this.currentFrame.values.get(i)); + for ( int i = 0; i < this.currentFrame.scopes.size(); i++ ) + addInitializer(i, targetFrame.scopes, this.currentFrame.getscopeobject(i)); + } + + + private void addInitializer(final int i, ArrayList<T> target, T value) + { + if ( target.size() <= i ) + { + adjustSize(target, i); + target.add(value); + } + else if ( target.get(i) instanceof TreeModelVisitor.IMergePoint<?> ) + { + @SuppressWarnings("unchecked") + TreeModelVisitor.IMergePoint<T> phi = (TreeModelVisitor.IMergePoint<T>) target.get(i); + phi.addValue(value); + } + else if ( target.get(i) == null ) + { + target.set(i, value); + } + else + { + // TODO: Verify that the existing value and the current value are the same. + } + } + + /** + * Build an operands Collection from a single operand. + * @param operand - the operand. + * @return the operand, wrapped in a Collection. + */ + private <X> Collection<X> singleOperand(X operand) + { + ArrayList<X> result = new ArrayList<X>(1); + result.add(operand); + return result; + } + + /** + * @return an empty list, approprately cast. + */ + private Collection<T> noOperands() + { + return Collections.emptyList(); + } + } + + /** + * Adjust the size of a collection of frame elements. + * @param frameElements - the frame elements of interest. + * @param idx - the minimum size of the frame element. + */ + private static <X> void adjustSize(ArrayList<X> frameElements, int idx) + { + while(frameElements.size() < idx ) + frameElements.add(null); + } + + /** + * Pop a value off a collection of frame elements. + * @param frameElements - the frame elements of interest. + * @return the last element in the collection, which + * has been removed from the collection. + */ + static <X> X popElement(ArrayList<X> frameElements) + { + int lastIdx = frameElements.size() - 1; + return frameElements.remove(lastIdx); + } +}
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/abc/models/TreeModelVisitor.java ---------------------------------------------------------------------- diff --git a/compiler/src/main/java/org/apache/flex/abc/models/TreeModelVisitor.java b/compiler/src/main/java/org/apache/flex/abc/models/TreeModelVisitor.java new file mode 100644 index 0000000..a11b0e6 --- /dev/null +++ b/compiler/src/main/java/org/apache/flex/abc/models/TreeModelVisitor.java @@ -0,0 +1,149 @@ +/* + * + * 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.models; + +import java.util.Collection; + +import org.apache.flex.abc.graph.IBasicBlock; +import org.apache.flex.abc.semantics.Instruction; +import org.apache.flex.abc.semantics.Name; + +/** + * A TreeModelVisitor translates stack-oriented + * operations into a operation/operands view of + * an ABC method body. + */ +public interface TreeModelVisitor<T> +{ + /** + * Begin visiting a method's frames. + * @param model - the TreeModelEncoder that's + * driving the visitor. + */ + public void visit(TreeModelEncoder<T> model); + + /** + * Finish visiting a method and its frames. + */ + public void visitEnd(); + + /** + * Begin a visit to a block. + * @param b the IBasicBlock to be visited. + * @return true if the encoder should continue visiting the block. + */ + public boolean visitBlock(IBasicBlock b); + + /** + * Finish visiting a block. + * @param b the last IBasicBlock to be confirmed to-be-visited by visitBlock(). + */ + public void visitEnd(IBasicBlock b); + + /** + * Translate an Instruction and its operands into + * an intermediate form. + * @param insn - the Instruction that encoded this operation. + * @param operands - the Instruction's operands, usually from + * the ABC value stack. + * @return an equivalent representation of the + * operation denoted by the instruction/operands tuple. + */ + public T translate(Instruction insn, Collection<T> operands); + + /** + * Translate a branch Instruction and its targets into + * an intermediate form. + * @param insn - the Instruction that encoded this operation. + * @param targets - the Instruction's targets. + * @return an equivalent representation of the + * operation denoted by the instruction/targets tuple. + */ + public T translateBranch(Instruction insn, Collection<IBasicBlock> targets); + + /** + * Recover from a value stack underflow condition. + * @param insn - the Instruction that attempted to encode + * this operation. + * @param count - the number of stack values required. + * @return a representation of the recovery action; + * returning some type of "bottom value" is recommended. + */ + public T valueStackUnderflow(Instruction insn, int count); + + /** + * Recover from a scope stack underflow condition. + * @param insn - the Instruction that attempted to encode + * this operation. + * @param count - the number of scope stack values required. + * @return a representation of the recovery action; + * returning some type of "bottom value" is recommended. + */ + public T scopeStackUnderflow(Instruction insn, int count); + + /** + * Get a representation of a function actual parameter. + * @param paramNumber - the parameter number. + * @return a representation of the parameter as an + * initializing value. + */ + public T translateParameter(int paramNumber); + + /** + * Get a representation of an exception variable. + * @param varName the exception variable's name. + * @param varType the exception variable's type. + * @return a representation of the exception + * variable as an initializing value. + */ + public T translateExceptionVariable(Name varName, Name varType); + + /** + * Add a merge node, where values from several blocks + * combine in the dataflow graph. + * @param toInit - the Block where these values combine. + * @return a representation of the merge point; this + * object must implement @code{IMergePoint<T>}. + */ + public T /* must implement IMergePoint<T> */ addMergePoint(IBasicBlock toInit); + + /** + * IMergePoint models a point where several predecessors' + * values combine in the dataflow graph. + */ + public interface IMergePoint<T> + { + /** + * Add a predecessor's value to the collection + * of predecessor values. The implementation + * is allowed to merge equal values, but may + * also choose not to do so. + * @param value - the new precedecessor value. + */ + public void addValue(T value); + + /** + * Get the values that predecessor blocks contributed + * to this dataflow merge point. + * @return the collection of predecessor values. + */ + public Collection<T> getValues(); + } +} http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/abc/models/package.html ---------------------------------------------------------------------- diff --git a/compiler/src/main/java/org/apache/flex/abc/models/package.html b/compiler/src/main/java/org/apache/flex/abc/models/package.html new file mode 100644 index 0000000..8f97e93 --- /dev/null +++ b/compiler/src/main/java/org/apache/flex/abc/models/package.html @@ -0,0 +1,29 @@ +<!-- + + 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 higher-level models of a method body +and encoders to translate from the basic ABC model +into these higher-level models. + +</body> +</html> + http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/abc/optimize/DeadCodeFilter.java ---------------------------------------------------------------------- diff --git a/compiler/src/main/java/org/apache/flex/abc/optimize/DeadCodeFilter.java b/compiler/src/main/java/org/apache/flex/abc/optimize/DeadCodeFilter.java new file mode 100644 index 0000000..0c2e74c --- /dev/null +++ b/compiler/src/main/java/org/apache/flex/abc/optimize/DeadCodeFilter.java @@ -0,0 +1,156 @@ +/* + * + * 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 java.util.Iterator; +import java.util.List; + +import org.apache.flex.abc.ABCConstants; +import org.apache.flex.abc.graph.IBasicBlock; +import org.apache.flex.abc.graph.IFlowgraph; +import org.apache.flex.abc.semantics.ExceptionInfo; +import org.apache.flex.abc.semantics.Instruction; +import org.apache.flex.abc.semantics.InstructionFactory; +import org.apache.flex.abc.semantics.MethodBodyInfo; +import org.apache.flex.abc.visitors.DelegatingMethodBodyVisitor; +import org.apache.flex.abc.visitors.IDiagnosticsVisitor; +import org.apache.flex.abc.visitors.IMethodBodyVisitor; + +/** + * DeadCodeFilter rebuilds the method's result InstructionList by walking the + * control flow graph at visitEnd() time, and resets its delegate's instructions + * to the pruned InstructionList. + */ +public class DeadCodeFilter extends DelegatingMethodBodyVisitor +{ + /** + * Constructor. + * + * @param mbi - the MethodBodyInfo to be analyzed. + * @param delegate - the next IMethodBodyVisitor in the chain. + */ + public DeadCodeFilter(MethodBodyInfo mbi, IMethodBodyVisitor delegate, IDiagnosticsVisitor diagnostics) + { + super(delegate); + this.mbi = mbi; + this.diagnostics = diagnostics; + } + + /** + * The MethodBodyInfo under analysis. + */ + protected final MethodBodyInfo mbi; + + /** + */ + protected final IDiagnosticsVisitor diagnostics; + + /** + * Walk the control flow graph and remove unreachable blocks. + */ + @Override + public void visitEnd() + { + IFlowgraph cfg = this.mbi.getCfg(); + List<IBasicBlock> blocks = cfg.getBlocksInEntryOrder(); + boolean lastBlockWasReachable = true; + + int blockIdx = 0; + while ( blockIdx < blocks.size() ) + { + IBasicBlock b = blocks.get(blockIdx); + boolean isReachable = cfg.isReachable(b); + + // Only advance the block index if the current + // block is removed. + int previousBlockCount = blocks.size(); + + if ( ! isReachable ) + { + // Don't remove unreachable blocks that are the final block in an exception handler, + // unless they're also the first block in the exception handler. The AVM depends on + // these blocks under some circumstances. However, the block's instructions can be + // coalesced to a single OP_nop. + boolean safeToRemove = true; + + for ( ExceptionInfo ex: this.mbi.getExceptions() ) + { + IBasicBlock toBlock = this.mbi.getCfg().getBlock(ex.getTo()); + if ( b.equals(toBlock) ) + { + IBasicBlock fromBlock = this.mbi.getCfg().getBlock(ex.getFrom()); + + int tryFrom = blocks.indexOf(fromBlock); + int tryTo = blocks.indexOf(toBlock); + assert tryFrom >= 0 && tryTo >= tryFrom; + + for ( int j = tryTo - 1; safeToRemove && j >= tryFrom; j-- ) + safeToRemove = !cfg.isReachable(blocks.get(j)); + + if ( !safeToRemove ) + { + // Can't remove it, but compact it: remove executable + // instructions, then write a single OP_nop as necessary. + + Iterator<Instruction> it = b.getInstructions().iterator(); + + while ( it.hasNext() ) + { + Instruction insn = it.next(); + + if ( insn.isExecutable() ) + it.remove(); + } + + b.getInstructions().add(InstructionFactory.getInstruction(ABCConstants.OP_nop)); + break; + } + } + } + + if ( safeToRemove ) + { + // Only remove the Block if it contains executable and non-NOP instructions. + for ( int j = 0; j < b.size(); j++ ) + { + Instruction insn = b.get(j); + if ( insn.isExecutable() && insn.getOpcode() != ABCConstants.OP_nop ) + { + // Only emit a diagnostic if b is the first unreachable block + // encountered in this sequence. + if ( lastBlockWasReachable ) + this.diagnostics.unreachableBlock(this.mbi, this.mbi.getCfg(), b); + cfg.removeUnreachableBlock(b); + break; + } + } + } + } + + if ( previousBlockCount == blocks.size() ) + blockIdx++; + + // Remember the state of the last-visited block. + lastBlockWasReachable = isReachable; + } + + super.visitEnd(); + } +}