/* * * 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(); } }