Package org.jnode.vm.compiler

Source Code of org.jnode.vm.compiler.OptimizingBytecodeVisitor$OptFlags

/*
* $Id$
*
* Copyright (C) 2003-2014 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.vm.compiler;

import org.jnode.vm.JvmType;
import org.jnode.vm.bytecode.BasicBlock;
import org.jnode.vm.bytecode.ControlFlowGraph;
import org.jnode.vm.classmgr.VmByteCode;
import org.jnode.vm.classmgr.VmClassLoader;
import org.jnode.vm.classmgr.VmConstClass;
import org.jnode.vm.classmgr.VmConstMethodRef;
import org.jnode.vm.classmgr.VmMethod;
import org.jnode.vm.classmgr.VmPrimitiveClass;
import org.jnode.vm.classmgr.VmType;
import org.jnode.vm.facade.VmUtils;
import org.jnode.vm.objects.Counter;

/**
* @author Ewout Prangsma (epr@users.sourceforge.net)
*/
public final class OptimizingBytecodeVisitor extends
    VerifyingCompilerBytecodeVisitor<InlineBytecodeVisitor> {

    /**
     * Maximum length of methods that will be inlined
     */
    private static final int SIZE_LIMIT = 32;

    /**
     * Maximum depth of recursive inlining
     */
    private static final int MAX_INLINE_DEPTH = -1; //5;

    /**
     * Common method entrypoints
     */
    private final EntryPoints entryPoints;

    /**
     * The classloader
     */
    private final VmClassLoader loader;

    /**
     * The method that is currently being visited
     */
    private VmMethod method;

    /**
     * The current max locals of method (adjusted for inlined methods)
     */
    private char maxLocals;

    /**
     * Diff to add to local indexes
     */
    private char localDelta;

    /**
     * Has a return been visited during an inline
     */
    private boolean visitedReturn = false;

    /**
     * How many nested inlines we're currently in (0 == no inline)
     */
    private byte inlineDepth = 0;

    // Inter instruction optimization flags

    /**
     * Optimization flag constants.
     */
    private static interface OptFlags {
        int ASTORE = 0x0001;

        int ISTORE = 0x0002;

        int LSTORE = 0x0004;

        int FSTORE = 0x0008;

        int DSTORE = 0x0010;

        int ALOAD = 0x0020;

        int ILOAD = 0x0040;

        int LLOAD = 0x0080;

        int FLOAD = 0x0100;

        int DLOAD = 0x0200;
    }

    /**
     * Optimize flags set by the current instruction
     */
    private int optimizeFlags;

    /**
     * Optimize flags set by the previous instruction
     */
    private int previousOptimizeFlags;

    /**
     * Index of the last xstore instruction
     */
    private int storeIndex;

    /**
     * Index of the last xload instruction
     */
    private int loadIndex;

    /**
     * Statistic counter for #inlined invokespecial's
     */
    private static Counter inlineSpecialCounter = VmUtils.getVm().getCounter(
        "inlined-invokespecial");

    /**
     * Statistic counter for #inlined invokespecial's
     */
    private static Counter inlineStaticCounter = VmUtils.getVm().getCounter(
        "inlined-invokestatic");

    /**
     * Statistic counter for #inlined invokespecial's
     */
    private static Counter inlineVirtualCounter = VmUtils.getVm().getCounter(
        "inlined-invokevirtual");

    /**
     * Statistic counter for astore/aload sequence
     */
    private static Counter storeLoadCounter = VmUtils.getVm().getCounter(
        "store-load");

    /**
     * Initialize this instance.
     *
     * @param entryPoints
     * @param delegate
     * @param loader
     */
    public OptimizingBytecodeVisitor(EntryPoints entryPoints, InlineBytecodeVisitor delegate, VmClassLoader loader) {
        super(delegate);
        this.entryPoints = entryPoints;
        this.loader = loader;
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#startMethod(org.jnode.vm.classmgr.VmMethod)
     */
    public void startMethod(VmMethod method) {
        this.method = method;
        this.maxLocals = (char) method.getBytecode().getNoLocals();
        // Reset optimization flags
        this.optimizeFlags = 0;
        this.previousOptimizeFlags = 0;
        // Delegate call
        super.startMethod(method);
    }

    /**
     * @see org.jnode.vm.compiler.DelegatingCompilerBytecodeVisitor#startBasicBlock(org.jnode.vm.bytecode.BasicBlock)
     */
    public void startBasicBlock(BasicBlock bb) {
        // Reset optimize flags
        this.previousOptimizeFlags = 0;
        this.optimizeFlags = 0;
        // Delegate call
        super.startBasicBlock(bb);
    }

    /**
     * @see org.jnode.vm.compiler.DelegatingCompilerBytecodeVisitor#startInstruction(int)
     */
    public void startInstruction(int address) {
        // Set previos optimize flags & clear optimize flags
        this.previousOptimizeFlags = this.optimizeFlags;
        this.optimizeFlags = 0;
        // Delegate call
        super.startInstruction(address);
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_invokespecial(org.jnode.vm.classmgr.VmConstMethodRef)
     */
    public void visit_invokespecial(VmConstMethodRef methodRef) {
        methodRef.resolve(loader);
        final VmMethod im = methodRef.getResolvedVmMethod();
        if (!canInline(im)) {
            // Do not inline this call
            super.visit_invokespecial(methodRef);
        } else {
            verifyInvoke(methodRef);
            inlineSpecialCounter.inc();
            inline(im);
        }
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_invokestatic(org.jnode.vm.classmgr.VmConstMethodRef)
     */
    public void visit_invokestatic(VmConstMethodRef methodRef) {
        methodRef.resolve(loader);
        final VmMethod im = methodRef.getResolvedVmMethod();
        if (!canInline(im)) {
            // Do not inline this call
            super.visit_invokestatic(methodRef);
        } else {
            verifyInvoke(methodRef);
            inlineStaticCounter.inc();
            inline(im);
        }
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_invokevirtual(org.jnode.vm.classmgr.VmConstMethodRef)
     */
    public void visit_invokevirtual(VmConstMethodRef methodRef) {
        methodRef.resolve(loader);
        final VmMethod im = methodRef.getResolvedVmMethod();
        if (!canInline(im)) {
            // Do not inline this call
            super.visit_invokevirtual(methodRef);
        } else {
            verifyInvoke(methodRef);
            inlineVirtualCounter.inc();
            inline(im);
        }
    }

    /**
     * Inline the given method into the current method
     *
     * @param im
     */
    private void inline(VmMethod im) {
        // Save some variables
        final char oldLocalDelta = this.localDelta;
        final boolean oldVisitedReturn = this.visitedReturn;
        final VmMethod oldMethod = this.method;
        final InlineBytecodeVisitor ibv = getDelegate();
        final VmByteCode bc = im.getBytecode();

        // Calculate the new maxLocals
        final int imLocals = bc.getNoLocals(); // #Locals of the inlined method
        final int curLocals = oldMethod.getBytecode().getNoLocals(); // #Locals
        // of
        // the
        // current
        // method
        maxLocals = (char) Math.max(maxLocals, oldLocalDelta + curLocals
            + imLocals);

        // Set new variables
        this.localDelta += curLocals;
        this.visitedReturn = false;
        this.inlineDepth++;
        this.method = im;

        // Reset optimization flags
        this.optimizeFlags = 0;
        this.previousOptimizeFlags = 0;

        // Start the inlining
        ibv.startInlinedMethodHeader(im, maxLocals);

        // Store the arguments in the locals of the inlined method
        storeArgumentsToLocals(im, ibv, localDelta);

        // Start the inlining
        ibv.startInlinedMethodCode(im, maxLocals);

        // Emit a NOP so we can differentiate when a method is virtually empty
        if (inlineDepth > 1) {
            ibv.visit_nop();
        }

        // Create the control flow graph
        ControlFlowGraph cfg = (ControlFlowGraph) bc.getCompilerData();
        if (cfg == null) {
            cfg = new ControlFlowGraph(bc);
            bc.setCompilerData(cfg);
        }
        // Compile the code 1 basic block at a time
        final CompilerBytecodeParser parser = new CompilerBytecodeParser(bc,
            cfg, this);
        for (BasicBlock bb : cfg) {
            this.startBasicBlock(bb);
            parser.parse(bb.getStartPC(), bb.getEndPC(), false);
            this.endBasicBlock();
        }

        if (!this.isReturnVisited()) {
            // Generate a dummy return to avoid breaking the compilers
            createDummyReturn(im, this);
        }

        // End the inlining
        ibv.endInlinedMethod(oldMethod);

        // Restore variables
        this.localDelta = oldLocalDelta;
        this.visitedReturn = oldVisitedReturn;
        this.inlineDepth--;
        this.method = oldMethod;

        // Reset optimization flags
        this.optimizeFlags = 0;
        this.previousOptimizeFlags = 0;
    }

    private void createDummyReturn(VmMethod im, CompilerBytecodeVisitor bcv) {
        if (im.isReturnVoid()) {
            bcv.visit_return();
        } else if (im.isReturnObject()) {
            bcv.visit_aconst_null();
            bcv.visit_areturn();
        } else if (im.isReturnWide()) {
            if (im.getReturnType().getJvmType() == JvmType.DOUBLE) {
                // double
                bcv.visit_dconst(0.0);
                bcv.visit_dreturn();
            } else {
                // long
                bcv.visit_lconst(0);
                bcv.visit_lreturn();
            }
        } else {
            if (im.getReturnType().getJvmType() == JvmType.FLOAT) {
                // float
                bcv.visit_fconst(0.0f);
                bcv.visit_freturn();
            } else {
                // int
                bcv.visit_iconst(0);
                bcv.visit_ireturn();
            }
        }
    }

    /**
     * Pop the method arguments of the stack and store them in locals.
     *
     * @param im
     * @param ibv
     * @param localDelta
     */
    private void storeArgumentsToLocals(VmMethod im, InlineBytecodeVisitor ibv,
                                        int localDelta) {
        final int cnt = im.getNoArguments();
        int local = localDelta + im.getArgSlotCount() - 1;
        /*
         * if (im.isStatic()) { local--; }
         */

        for (int i = cnt - 1; i >= 0; i--) {
            final VmType<?> argType = im.getArgumentType(i);
            // System.out.println("arg" + i + ": " + argType);

            if (argType.isPrimitive()) {
                final VmPrimitiveClass<?> pc = (VmPrimitiveClass<?>) argType;
                if (pc.isWide()) {
                    local--;
                    if (pc.isFloatingPoint()) {
                        // double
                        ibv.visit_dstore(local);
                    } else {
                        // long
                        ibv.visit_lstore(local);
                    }
                } else {
                    if (pc.isFloatingPoint()) {
                        // float
                        ibv.visit_fstore(local);
                    } else {
                        // int
                        ibv.visit_istore(local);
                    }
                }
            } else {
                ibv.visit_astore(local);
            }
            local--;
        }

        if (!im.isStatic()) {
            // TODO add nullpointer check

            // Store this pointer.
            ibv.visit_astore(local);
        }
    }

    /**
     * Can the given method be inlined?
     *
     * @param method
     * @return
     */
    private boolean canInline(VmMethod method) {

        // First determine if we CAN inline
        if (method.isNative() || method.isAbstract() || method.isSynchronized()) {
            return false;
        }
        if (!(method.isFinal() || method.isPrivate() || method.isStatic() || method
            .getDeclaringClass().isFinal())) {
            return false;
        }
        final VmType<?> declClass = method.getDeclaringClass();
        if (declClass.isMagicType()) {
            return false;
        }
        if (!declClass.isAlwaysInitialized()) {
            return false;
        }
        final VmByteCode bc = method.getBytecode();
        if (bc == null) {
            return false;
        }
        if (method.hasNoInlinePragma()) {
            return false;
        }
        if (bc.getNoExceptionHandlers() > 0) {
            return false;
        }

        // Now determine if we SHOULD inline
        if (!method.hasInlinePragma()) {
            if (inlineDepth >= MAX_INLINE_DEPTH) {
                return false;
            }
            if (bc.getLength() > SIZE_LIMIT) {
                return false;
            }
        }
        return true;
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_aload(int)
     */
    public void visit_aload(int index) {
        index += localDelta;
        if (((previousOptimizeFlags & OptFlags.ASTORE) != 0)
            && (storeIndex == index)) {
            storeLoadCounter.inc();
            super.visit_aloadStored(index);
        } else if (((previousOptimizeFlags & OptFlags.ALOAD) != 0)
            && (loadIndex == index)) {
            super.visit_dup();
        } else {
            super.visit_aload(index);
        }
        loadIndex = index;
        optimizeFlags |= OptFlags.ALOAD;
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_areturn()
     */
    public void visit_areturn() {
        if (inlineDepth == 0) {
            super.visit_areturn();
        } else {
            visitedReturn = true;
            getDelegate().visit_inlinedReturn(JvmType.REFERENCE);
        }
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_astore(int)
     */
    public void visit_astore(int index) {
        index += localDelta;
        this.optimizeFlags |= OptFlags.ASTORE;
        this.storeIndex = index;
        super.visit_astore(index);
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dload(int)
     */
    public void visit_dload(int index) {
        index += localDelta;
        if (((previousOptimizeFlags & OptFlags.DSTORE) != 0)
            && (storeIndex == index)) {
            storeLoadCounter.inc();
            super.visit_dloadStored(index);
        } else if (((previousOptimizeFlags & OptFlags.DLOAD) != 0)
            && (loadIndex == index)) {
            super.visit_dup2();
        } else {
            super.visit_dload(index);
        }
        loadIndex = index;
        optimizeFlags |= OptFlags.DLOAD;
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dreturn()
     */
    public void visit_dreturn() {
        if (inlineDepth == 0) {
            super.visit_dreturn();
        } else {
            visitedReturn = true;
            getDelegate().visit_inlinedReturn(JvmType.DOUBLE);
        }
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dstore(int)
     */
    public void visit_dstore(int index) {
        index += localDelta;
        this.optimizeFlags |= OptFlags.DSTORE;
        this.storeIndex = index;
        super.visit_dstore(index);
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_fload(int)
     */
    public void visit_fload(int index) {
        index += localDelta;
        if (((previousOptimizeFlags & OptFlags.FSTORE) != 0)
            && (storeIndex == index)) {
            storeLoadCounter.inc();
            super.visit_floadStored(index);
        } else if (((previousOptimizeFlags & OptFlags.FLOAD) != 0)
            && (loadIndex == index)) {
            super.visit_dup();
        } else {
            super.visit_fload(index);
        }
        loadIndex = index;
        optimizeFlags |= OptFlags.FLOAD;
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_freturn()
     */
    public void visit_freturn() {
        if (inlineDepth == 0) {
            super.visit_freturn();
        } else {
            visitedReturn = true;
            getDelegate().visit_inlinedReturn(JvmType.FLOAT);
        }
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_fstore(int)
     */
    public void visit_fstore(int index) {
        index += localDelta;
        this.optimizeFlags |= OptFlags.FSTORE;
        this.storeIndex = index;
        super.visit_fstore(index);
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_iinc(int, int)
     */
    public void visit_iinc(int index, int incValue) {
        super.visit_iinc(index + localDelta, incValue);
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_iload(int)
     */
    public void visit_iload(int index) {
        index += localDelta;
        if (((previousOptimizeFlags & OptFlags.ISTORE) != 0)
            && (storeIndex == index)) {
            storeLoadCounter.inc();
            super.visit_iloadStored(index);
        } else if (((previousOptimizeFlags & OptFlags.ILOAD) != 0)
            && (loadIndex == index)) {
            super.visit_dup();
        } else {
            super.visit_iload(index);
        }
        loadIndex = index;
        optimizeFlags |= OptFlags.ILOAD;
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ireturn()
     */
    public void visit_ireturn() {
        if (inlineDepth == 0) {
            super.visit_ireturn();
        } else {
            visitedReturn = true;
            getDelegate().visit_inlinedReturn(JvmType.INT);
        }
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_istore(int)
     */
    public void visit_istore(int index) {
        index += localDelta;
        this.optimizeFlags |= OptFlags.ISTORE;
        this.storeIndex = index;
        super.visit_istore(index);
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lload(int)
     */
    public void visit_lload(int index) {
        index += localDelta;
        if (((previousOptimizeFlags & OptFlags.LSTORE) != 0)
            && (storeIndex == index)) {
            storeLoadCounter.inc();
            super.visit_lloadStored(index);
        } else if (((previousOptimizeFlags & OptFlags.LLOAD) != 0)
            && (loadIndex == index)) {
            super.visit_dup2();
        } else {
            super.visit_lload(index);
        }
        loadIndex = index;
        optimizeFlags |= OptFlags.LLOAD;
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lreturn()
     */
    public void visit_lreturn() {
        if (inlineDepth == 0) {
            super.visit_lreturn();
        } else {
            visitedReturn = true;
            getDelegate().visit_inlinedReturn(JvmType.LONG);
        }
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lstore(int)
     */
    public void visit_lstore(int index) {
        index += localDelta;
        this.optimizeFlags |= OptFlags.LSTORE;
        this.storeIndex = index;
        super.visit_lstore(index);
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ret(int)
     */
    public void visit_ret(int index) {
        super.visit_ret(index + localDelta);
    }

    /**
     * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_return()
     */
    public void visit_return() {
        if (inlineDepth == 0) {
            super.visit_return();
        } else {
            visitedReturn = true;
            getDelegate().visit_inlinedReturn(JvmType.VOID);
        }
    }

    /**
     * Have we visited a return statement?
     *
     * @return {@code true} if we have visited a return statement, {code false} otherwise.
     */
    public boolean isReturnVisited() {
        return visitedReturn;
    }

    /**
     * @see org.jnode.vm.compiler.DelegatingCompilerBytecodeVisitor#visit_monitorenter()
     */
    public void visit_monitorenter() {
        verifyMonitor();
        inline(entryPoints.getMonitorEnterMethod());
    }

    /**
     * @see org.jnode.vm.compiler.DelegatingCompilerBytecodeVisitor#visit_monitorexit()
     */
    public void visit_monitorexit() {
        verifyMonitor();
        inline(entryPoints.getMonitorExitMethod());
    }

    /**
     * @see org.jnode.vm.compiler.DelegatingCompilerBytecodeVisitor#visit_new(org.jnode.vm.classmgr.VmConstClass)
     */
    public void visit_new(VmConstClass clazz) {
        if (false) {
            // Inline call to {@link SoftByteCodes#allocObject}
            clazz.resolve(loader);

            /* Setup a call to SoftByteCodes.allocObject */
            visit_ldc(clazz.getResolvedVmClass()); // vmClass
            visit_iconst(-1); // size
            inline(entryPoints.getAllocObjectMethod());
        } else {
            super.visit_new(clazz);
        }
    }
}
TOP

Related Classes of org.jnode.vm.compiler.OptimizingBytecodeVisitor$OptFlags

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.