bytecode.BlockExporter.java Source code

Java tutorial

Introduction

Here is the source code for bytecode.BlockExporter.java

Source

/*
 * Parallelising JVM Compiler
 *
 * Copyright 2010 Peter Calvert, University of Cambridge
 *
 * Licensed 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 bytecode;

import analysis.CodeTraverser;

import graph.BasicBlock;
import graph.Block;
import graph.BlockVisitor;
import graph.CodeVisitor;
import graph.Loop;

import graph.TrivialLoop;
import graph.instructions.Call;
import graph.instructions.Instruction;
import graph.instructions.Read;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;

import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

/**
 * 
 */
class BlockExporter extends BlockVisitor<Void> {
    /**
     * Cache of instruction flattenings for a basic block.
     *
     * TODO: Change this to be more clever (i.e. map from something other than
     *       blocks). Would remove need for 'invalidate' function.
     */
    static private Map<BasicBlock, List<Instruction>> cached = new WeakHashMap<BasicBlock, List<Instruction>>();

    /**
     * Mapping from graph blocks to labels.
     */
    static private Map<Block, Label> labels = new HashMap<Block, Label>();

    /**
     * Blocks that have already been exported.
     */
    private Set<Block> visited = new HashSet<Block>();

    /**
     * ASM library method visitor to which export should occur.
     */
    private MethodVisitor mv;

    /**
     * Default destination (in case of null jumps).
     */
    private Block defaultDestination = null;

    /**
     * Constructs an exporter for the given export destination.
     *
     * @param mv     ASM method visitor to which the blocks should be exported.
     */
    public BlockExporter(MethodVisitor mv) {
        this.mv = mv;
    }

    /**
     * Constructs an exporter for the given export destination, along with a
     * default destination for null jumps. This is useful for loop exports where
     * the end of the loop
     *
     * @param mv     ASM method visitor to which the blocks should be exported.
     */
    public BlockExporter(MethodVisitor mv, Block dflt) {
        this.mv = mv;
        this.defaultDestination = dflt;
    }

    /**
     * Caches a given flattened order of instructions for a basic block.
     *
     * @param bb     Basic block to cache.
     * @param c      Ordered, flattened code to cache.
     */
    public static void cacheCode(BasicBlock bb, List<Instruction> c) {
        cached.put(bb, c);
    }

    /**
     * Invalidate the cache for a given basic block.
     *
     * @param bb     Basic block to invalidate the cache for.
     */
    public static void invalidate(BasicBlock bb) {
        cached.remove(bb);
    }

    /**
     * Returns a label for a block, creating a new one if one hasn't yet been
     * allocated.
     *
     * @param b      Block to get label for.
     * @return       ASM label.
     */
    static public Label getLabel(Block b) {
        if (labels.containsKey(b))
            return labels.get(b);

        // Create Label
        Label l = new Label();
        labels.put(b, l);

        return l;
    }

    /**
     * Exports the given basic block to the export destination. Where possible a
     * cached flattening of the block code is used. Otherwise a depth first
     * traversal of the code is used (blindly!).
     *
     * @param bb     Basic block to export.
     * @return       <code>null</code>
     */
    @Override
    public Void visit(BasicBlock bb) {
        // Ensure we haven't already exported this block.
        if (visited.contains(bb))
            return null;
        visited.add(bb);

        // Label name
        mv.visitLabel(getLabel(bb));

        // Debug line number (if available)
        if (bb.getLineNumber() != null) {
            mv.visitLineNumber(bb.getLineNumber().intValue(), getLabel(bb));
        }

        if (defaultDestination != null)
            labels.put(null, getLabel(defaultDestination));

        // Cached copy available.
        if (cached.containsKey(bb)) {
            for (Instruction i : cached.get(bb)) {
                i.accept(new InstructionExporter(mv));
            }
            // Generate code by traversal.
        } else {
            CodeVisitor<Void> cv = new CodeTraverser(new InstructionExporter(mv));

            for (Instruction i : bb.getStateful()) {
                // TODO: Is this reasonable?
                if (!(i instanceof Read) || ((i instanceof Call) && (((Call) i).getType().getSort() == null))) {
                    i.accept(cv);
                }
            }

            if (bb.getBranch() != null) {
                bb.getBranch().accept(cv);
            }
        }

        labels.remove(null);

        // Generate jump to next label.
        if (bb.getNext() != null) {
            if (visited.contains(bb.getNext())) {
                mv.visitJumpInsn(Opcodes.GOTO, getLabel(bb.getNext()));
            } else {
                bb.getNext().accept(this);
            }
        } else if (defaultDestination != null) {
            mv.visitJumpInsn(Opcodes.GOTO, getLabel(defaultDestination));
        } else {
            // TODO: Assert branch is RETURN, SWITCH, THROW, ...
        }

        // Ensure successors are exported.
        visit(bb.getSuccessors());

        return null;
    }

    /**
     * Exports the given loop to the export destination. This simply exports the
     * loop body, looping its end back to the beginning of the loop.
     *
     * @param l      Loop to export
     * @return       <code>null</code>
     */
    @Override
    public Void visit(Loop l) {
        // Ensure we haven't already exported this block.
        if (visited.contains(l))
            return null;
        visited.add(l);

        // Label name
        mv.visitLabel(getLabel(l));

        // Debug line number (if available)
        if (l.getLineNumber() != null) {
            mv.visitLineNumber(l.getLineNumber().intValue(), getLabel(l));
        }

        // Export loop body, looping back to here at the end (default destination).
        BlockExporter be = new BlockExporter(mv, l);
        be.visited.addAll(l.getSuccessors());
        l.getStart().accept(be);

        // Ensure successors are exported.
        visit(l.getSuccessors());

        return null;
    }

    /**
     * Exports the given loop to the export destination. This simply exports the
     * loop body, looping its end back to the beginning of the loop.
     *
     * @param l      Loop to export
     * @return       <code>null</code>
     */
    @Override
    public Void visit(TrivialLoop t) {
        // Ensure we haven't already exported this block.
        if (visited.contains(t))
            return null;
        visited.add(t);

        // Label name
        mv.visitLabel(getLabel(t));

        // Debug line number (if available)
        if (t.getLineNumber() != null) {
            mv.visitLineNumber(t.getLineNumber().intValue(), getLabel(t));
        }

        // Condition code
        mv.visitVarInsn(Opcodes.ILOAD, t.getIndex().getIndex());
        t.getLimit().accept(new CodeTraverser(new InstructionExporter(mv)));

        if (t.getIncrements().get(t.getIndex()) > 0) {
            mv.visitJumpInsn(Opcodes.IF_ICMPGE, getLabel(t.getNext()));
        } else {
            mv.visitJumpInsn(Opcodes.IF_ICMPLE, getLabel(t.getNext()));
        }

        // Export loop body, looping back to here at the end (default destination).
        BlockExporter be = new BlockExporter(mv, t);
        be.visited.addAll(t.getSuccessors());
        t.getStart().accept(be);

        // Ensure successors are exported.
        visit(t.getSuccessors());

        return null;
    }
}