jaspex.speculation.newspec.RemoveOverspeculation.java Source code

Java tutorial

Introduction

Here is the source code for jaspex.speculation.newspec.RemoveOverspeculation.java

Source

/*
 * jaspex-mls: a Java Software Speculative Parallelization Framework
 * Copyright (C) 2015 Ivo Anjo <ivo.anjo@ist.utl.pt>
 *
 * This file is part of jaspex-mls.
 *
 * jaspex-mls is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * jaspex-mls 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with jaspex-mls.  If not, see <http://www.gnu.org/licenses/>.
 */

package jaspex.speculation.newspec;

import java.util.*;

import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import util.*;
import asmlib.*;

import jaspex.speculation.CommonTypes;
import jaspex.speculation.InvokedMethod;
import jaspex.speculation.runtime.CodegenHelper;

import static jaspex.speculation.newspec.DelayGetFutureMethodVisitor.returnOpcodes;

/** Classe tenta identificar e resolver casos de "overspeculation", em que  inserida uma especulao mas
  * rapidamente  encontrada uma instruco no-transaccional ou que precisa do valor de retorno do mtodo.
  *
  * Nota: Esta classe permite que cross-overspeculation venha a acontecer. Isto porque num caso como
  * int m() {
  *   spawnspeculation m2();
  *   return 1;
  * }
  * Fica um futuro pendente  saida de m(). O problema  que composto com
  * int m2() {
  *   spawnspeculation m3();
  *   return 0;
  * }
  * A execuo especulativa de m2() foi intil: consiste apenas em criar uma nova especulao para m3,
  * executar o return, e terminar a SpeculationTask.
  * Como resolver isto? Talvez adicionar um argumento extra os mtodos que indique se deve ou no ser
  * permitido um futuro ficar pendente (como indicar a profundidade da callstack?)?
  *
  * (Um caso especial so os mtodos que retornam void, que iriam sempre causar isto, e nesses no so
  * permitidos futuros pendentes).
  **/
public class RemoveOverspeculation {

    private static final Logger Log = LoggerFactory.getLogger(RemoveOverspeculation.class);

    public static boolean scanOverspeculation(InfoClass currentClass, ClassNode cNode,
            Map<InfoMethod, UtilList<Integer>> rejectedSpecIdsMap) {
        boolean changedMap = false;

        if (!jaspex.Options.NOREMOVEOVERSPEC)
            for (MethodNode m : cNode.methods) {
                if (!m.name.endsWith("$speculative") && (!m.name.equals("<init>")
                        || !m.desc.contains(CommonTypes.SPECULATIVECTORMARKER.bytecodeName()))) {
                    continue;
                }

                //Log.debug("Checking {}", m);
                RemoveOverspeculation ro = new RemoveOverspeculation(m);
                UtilList<Integer> rejectedSpecIds = ro._rejectedSynchronization;

                if (ro._foundSpawn) {
                    Log.trace("RemoveOverspeculation " + currentClass.type().commonName() + "." + m.name
                            + ": NeedValue " + ro._rejectedNeedValue + ", Sync " + ro._rejectedSynchronization);
                }

                // No modo agressivervp permitimos os "rejectedNeedValue" (ver scanMethod)
                if (!jaspex.Options.AGRESSIVERVP)
                    rejectedSpecIds.addAll(ro._rejectedNeedValue);

                if (!rejectedSpecIds.isEmpty()) {
                    InfoMethod currentMethod = currentClass.getMethod(m.name, m.desc);
                    UtilList<Integer> existingRejected = rejectedSpecIdsMap.get(currentMethod);

                    if (existingRejected == null) {
                        rejectedSpecIdsMap.put(currentMethod, rejectedSpecIds);
                        changedMap = true;
                    } else {
                        for (Integer i : rejectedSpecIds) {
                            if (!existingRejected.contains(i)) {
                                existingRejected.add(i);
                                changedMap = true;
                            }
                        }
                    }
                }
            }

        //Log.debug("RemoveOverspeculation result: {}", rejectedSpecIdsMap);

        return changedMap;
    }

    private final MethodNode _mNode;
    // Rejeitados devido a ser necessrio o valor do futuro (get)
    private final UtilList<Integer> _rejectedNeedValue = new UtilArrayList<Integer>();
    // Rejeitados devido a ser necessria sincronizao (nonTransactionalActionAttempted)
    private final UtilList<Integer> _rejectedSynchronization = new UtilArrayList<Integer>();
    //  feita especulao neste mtodo?
    private boolean _foundSpawn;

    private RemoveOverspeculation(MethodNode mNode) {
        _mNode = mNode;

        scanMethod();
    }

    private void scanMethod() {
        for (AbstractInsnNode node = _mNode.instructions.getFirst(); node != null; node = node.getNext()) {
            if (node instanceof MethodInsnNode) {
                MethodInsnNode mInsn = (MethodInsnNode) node;

                if (mInsn.owner.equals(CommonTypes.CONTSPECULATIONCONTROL.asmName())
                        && mInsn.name.equals("spawnSpeculation")) {
                    _foundSpawn = true;
                    Log.trace("  Tracking {} ({})", getFutureId(mInsn), getFutureType(mInsn));
                    TrackingResult res = scanFuture(mInsn, mInsn.getNext());
                    if (res != TrackingResult.OK) {
                        addOverspeculation(mInsn, res);
                    }
                }
            }

        }
    }

    /** Possveis retornos do scanFuture **/
    private enum TrackingResult {
        /** Especulao deve ser inserida **/
        OK,
        /** Overspeculation, precisamos do valor da especulao muito cedo **/
        NEEDVALUE,
        /** Overspeculation, especulao vai terminar muito cedo **/
        FORCEDSYNC;

        /** Junta dois TrackingResults, escolhendo o superset de ambos.
          * Quando existem vrios TrackingResults para um futuro (porque podem acontecer diferentes
          * coisas em diferentes caminhos pelo mtodo), temos que escolher um "superset" que determina
          * o valor a ser retornado pelo scanFuture.
          *
          * Neste momento a estratgia implementada pelo combine  a de "arriscar" e permitir
          * overspeculation, desde que um dos caminhos possveis no o seja. Talvez no futuro possa
          * ser adicionado outro modo mais conservador (sendo que basta alterar o combine).
          **/
        public static TrackingResult combine(TrackingResult tr1, TrackingResult tr2) {
            return tr1.compareTo(tr2) <= 0 ? tr1 : tr2;
        }
    }

    /** Scan simples para tentar eliminar overspeculation.
      *
      * A ideia deste mtodo  determinar se entre uma operao de spawnSpeculation e o get do seu Future /
      * nonTransactionalActionAttempted existem instruces suficientes para valer o trabalho de fazer
      * especulao.
      *
      * Quando observamos um spawnSpeculation, comeamos a fazer tracking dele. Se virmos alguma
      * operao complexa (alguns saltos, outros mtodos, etc), paramos o tracking. Caso contrrio, se
      * ainda estivermos a fazer tracking quando encontramos o get / nonTransactionalActionAttempted, ento
      * colocamos o mtodo na lista de especulaes a rejeitar.
      **/
    private TrackingResult scanFuture(MethodInsnNode tracking, AbstractInsnNode start) {
        for (AbstractInsnNode node = start; node != null; node = node.getNext()) {
            if (node instanceof MethodInsnNode) {
                MethodInsnNode mInsn = (MethodInsnNode) node;

                if (mInsn.owner.equals(CommonTypes.CONTSPECULATIONCONTROL.asmName())
                        && mInsn.name.equals("spawnSpeculation")) {
                    // do nothing
                } else if (mInsn.owner.startsWith(CommonTypes.FUTURE.asmName()) && mInsn.name.equals("get")) {
                    int futureId = Integer.parseInt(mInsn.owner.substring(mInsn.owner.lastIndexOf('$') + 1));

                    if (getFutureId(tracking) == futureId) {
                        return TrackingResult.NEEDVALUE;
                    }
                } else if (mInsn.owner.equals(CommonTypes.SPECULATIONCONTROL.asmName())
                        && (mInsn.name.equals("nonTransactionalActionAttempted")
                                || mInsn.name.equals("blacklistedActionAttempted"))) {
                    return TrackingResult.FORCEDSYNC;
                } else if (mInsn.owner.equals(CommonTypes.TRANSACTION.asmName())
                        || mInsn.owner.equals(CommonTypes.MARKER_BEFOREINLINEDSTORE)
                        || mInsn.owner.equals(CommonTypes.DEBUGCLASS.asmName())) {
                    continue;
                } else if (mInsn.owner.equals(CommonTypes.REPLACEMENTS.asmName())) {
                    // do nothing
                } else if (mInsn.owner.startsWith("jaspex")
                        && !CodegenHelper.isCodegenClass(Type.fromAsm(mInsn.owner))) {
                    throw new AssertionError("Resetting tracking due to call to " + mInsn.owner + "." + mInsn.name);
                }

                return TrackingResult.OK;
            } else if (node instanceof JumpInsnNode) {
                JumpInsnNode jInsn = (JumpInsnNode) node;

                if (_mNode.instructions.indexOf(jInsn) > _mNode.instructions.indexOf(jInsn.label)) {
                    // Salto para trs, no continuar tracking
                    return TrackingResult.OK;
                }

                if (jInsn.getOpcode() == Opcodes.GOTO)
                    return scanFuture(tracking, jInsn.label);
                if (jInsn.getOpcode() == Opcodes.JSR)
                    throw new AssertionError();

                // Opcode  um de
                // IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT,
                // IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE, IFNULL, IFNONNULL
                // Portanto vamos ter que analisar ambos os branches

                return TrackingResult.combine(scanFuture(tracking, jInsn.getNext()),
                        scanFuture(tracking, jInsn.label));
            } else if (node instanceof LookupSwitchInsnNode || node instanceof TableSwitchInsnNode) {
                List<LabelNode> targets = new UtilArrayList<LabelNode>();

                // Infelizmente o ASM no tem uma classe comum entre os dois tipos de switch
                if (node instanceof LookupSwitchInsnNode) {
                    LookupSwitchInsnNode sInsn = (LookupSwitchInsnNode) node;
                    if (sInsn.dflt != null)
                        targets.add(sInsn.dflt);
                    targets.addAll(sInsn.labels);
                } else {
                    TableSwitchInsnNode sInsn = (TableSwitchInsnNode) node;
                    if (sInsn.dflt != null)
                        targets.add(sInsn.dflt);
                    targets.addAll(sInsn.labels);
                }

                // Vamos analisar todos os targets do switch e fazer merge do resultado
                TrackingResult mergedResult = null;
                for (LabelNode l : targets) {
                    TrackingResult res = scanFuture(tracking, l);

                    if (mergedResult == null) {
                        mergedResult = res;
                    } else {
                        mergedResult = TrackingResult.combine(mergedResult, res);
                    }
                }

                return mergedResult;
            } else if (node instanceof InsnNode) {
                InsnNode insn = (InsnNode) node;

                // Encontrmos fim do mtodo
                if (insn.getOpcode() == Opcodes.ATHROW || returnOpcodes.contains(insn.getOpcode())) {
                    if (new InvokedMethod(_mNode).returnType().equals(Type.PRIM_VOID)) {
                        // Caso especial: Todos os mtodos que retornam void so
                        // escolhidos para especulao -- Uma consequncia directa
                        // disso  que ter um futuro activo aquando do retorno do
                        // mtodo no serve para nada, j que a nica coisa que vai
                        // acontecer  a especulao acabada de criar vai tentar fazer
                        // commit imediatamente
                        return TrackingResult.FORCEDSYNC;
                    }

                    return TrackingResult.OK;
                }
            }
        }

        // FIXME: Alguma vez aqui chegamos?
        //return TrackingResult.OK;
        throw new AssertionError("FIXME");
    }

    private void addOverspeculation(MethodInsnNode tracking, TrackingResult tr) {
        int specId = getFutureId(tracking);
        Type futureType = getFutureType(tracking);
        Log.trace("  Overspeculation detected for FutureId {} ({})", specId, futureType);
        if (tr == TrackingResult.NEEDVALUE && jaspex.Options.RVP
                && (futureType.equals(Type.PRIM_BOOLEAN) || futureType.equals(Type.OBJECT_BOOLEAN))) {
            // Permitir overspeculation em booleans quando estamos a usar RVP
        } else {
            if (tr == TrackingResult.NEEDVALUE) {
                _rejectedNeedValue.add(specId);
            } else {
                _rejectedSynchronization.add(specId);
            }
        }
    }

    private static int getFutureId(MethodInsnNode mInsn) {
        return FutureMetadata.fromBytecode(mInsn.desc.substring(mInsn.desc.indexOf(')') + 1)).id();
    }

    private static Type getFutureType(MethodInsnNode mInsn) {
        return FutureMetadata.fromBytecode(mInsn.desc.substring(mInsn.desc.indexOf(')') + 1)).returnType();
    }

}