jaspex.speculation.runtime.CodegenHelper.java Source code

Java tutorial

Introduction

Here is the source code for jaspex.speculation.runtime.CodegenHelper.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.runtime;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;

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

import jaspex.Options;
import jaspex.speculation.CommonTypes;
import jaspex.speculation.FixPrivateMethodAccessMethodVisitor;
import jaspex.speculation.InvokedMethod;
import jaspex.speculation.newspec.SpeculationSkiplist;
import util.UtilList;

import static jaspex.util.ShellColor.color;

import asmlib.Type;

/** Classe que gera classes wrapper para chamadas especulativas a mtodos.
  *
  * Em vez de se coleccionar os argumentos num array, e depois usar reflection para invocar um mtodo dentro do
  * ExecutionTask, geramos uma classe que guarda os argumentos em fields com o tipo correcto (evita boxing/unboxing)
  * e que tem um mtodo call, que executa o mtodo alvo, com os argumentos com que a instncia da classe wrapper
  * foi criada.
  **/
public final class CodegenHelper {

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

    /** Prefixo usado para classes codegen.
      * Derivado do nome do CodegenHelper, para permitir refactorizaes automticas.
      **/
    public static final String CODEGEN_CLASS_PREFIX = CodegenHelper.class.getPackage().getName()
            + ".codegen.Codegen$";

    /** Mappings entre mtodos a serem invocados usado especulao e ids. **/
    private static final Map<Integer, InvokedMethod> _idToMethodMap = new HashMap<Integer, InvokedMethod>();
    private static final Map<InvokedMethod, Integer> _methodToIdMap = new HashMap<InvokedMethod, Integer>();

    public static Map<Integer, InvokedMethod> saveCodegen() {
        return java.util.Collections.unmodifiableMap(_idToMethodMap);
    }

    public static void restoreCodegen(Map<Integer, InvokedMethod> idToMethodMap) {
        assert (_idToMethodMap.isEmpty());
        assert (_methodToIdMap.isEmpty());
        for (Map.Entry<Integer, InvokedMethod> entry : idToMethodMap.entrySet()) {
            Integer id = entry.getKey();
            InvokedMethod method = entry.getValue();
            _idToMethodMap.put(id, method);
            _methodToIdMap.put(method, id);
        }
    }

    /** Mantm um mapping entre InvokedMethods e inteiros.
      * Cada InvokedMethod nico tem um Id, e esse Id  incluido no nome da classe a ser gerada.
      **/
    public static Type methodToCodegenType(InvokedMethod method) {
        Integer id = _methodToIdMap.get(method);
        if (id == null) {
            id = _methodToIdMap.size();
            _methodToIdMap.put(method, id);
            _idToMethodMap.put(id, method);
        }
        return Type.fromCommon(CODEGEN_CLASS_PREFIX + id + "$" + method.owner().commonName().replace('.', '_') + "."
                + FixPrivateMethodAccessMethodVisitor.stripPrivate(method.name()));
    }

    /** Devolve id interno do codegen. Usar apenas para debugging **/
    public static int codegenId(InvokedMethod method) {
        Integer id = _methodToIdMap.get(method);
        if (id == null)
            return -1;
        return id;
    }

    public static boolean isCodegenClass(Type type) {
        return type.commonName().startsWith(CODEGEN_CLASS_PREFIX);
    }

    /** Usado para debugging, transforma nome de classe codegen de volta para o nome original **/
    public static String codegenToOriginal(String codegenName) {
        int endClassIndex = codegenName.lastIndexOf('.');
        return codegenName.substring(CODEGEN_CLASS_PREFIX.length(), endClassIndex).replace('_', '.') + codegenName
                .subSequence(endClassIndex, codegenName.length()).toString().replace("$speculative", "");
    }

    public static byte[] generateClass(Type codegenType) {
        // Obter id a partir do tipo
        String name = codegenType.commonName();
        name = name.replaceFirst(Matcher.quoteReplacement(CODEGEN_CLASS_PREFIX), "");
        int id = Integer.parseInt(name.substring(0, name.indexOf("$")));

        // Obter InvokedMethod
        InvokedMethod method = _idToMethodMap.get(id);

        Log.trace(color("[ CodeGen ]", "31") + " Generating wrapper for {}.{}{}", method.owner(), method.name(),
                method.desc());

        UtilList<Type> arguments = method.argumentTypes();
        if (!method.isStatic())
            arguments.addFirst(method.owner());
        String argumentsDesc = "";
        for (Type type : arguments)
            argumentsDesc += type.bytecodeName();

        // Optimizao: Se mtodo for ()V e static, criamos um singleton apenas com o call,
        //      e tornamos o constructor private para que a classe no seja instanciada
        //      noutros locais
        boolean singletonMode = arguments.isEmpty();

        // Criar classe
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        cw.visit(V1_6, ACC_PUBLIC | ACC_FINAL, codegenType.asmName(), null, CommonTypes.CALLABLE.asmName(),
                (Options.RVP ? new String[] { CommonTypes.PREDICTABLECALLABLE.asmName() } : null));
        cw.visitSource(color("JaSPEx Generated Wrapper Class", "31"), null);

        // Criar fields para conter argumentos
        {
            int fieldPos = 0;
            for (Type t : arguments) {
                cw.visitField(ACC_PRIVATE + ACC_FINAL, "arg" + (fieldPos++), t.bytecodeName(), null, null);
            }
        }

        // Criar constructor
        {
            MethodVisitor mv = cw.visitMethod((singletonMode ? ACC_PRIVATE : ACC_PUBLIC), "<init>",
                    "(" + argumentsDesc + ")V", null, null);
            mv.visitCode();
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESPECIAL, Type.OBJECT.asmName(), "<init>", "()V");
            int localsPos = 0;
            int fieldPos = 0;
            for (Type t : arguments) {
                mv.visitVarInsn(ALOAD, 0);
                mv.visitVarInsn(t.getLoadInsn(), localsPos + 1);
                mv.visitFieldInsn(PUTFIELD, codegenType.asmName(), "arg" + fieldPos++, t.bytecodeName());
                localsPos += t.getNumberSlots();
            }
            mv.visitInsn(RETURN);
            mv.visitMaxs(0, 0);
            mv.visitEnd();
        }

        // Criar mtodo esttico como "bridge" para o constructor
        // A ideia deste mtodo  resolver o problema de j termos os argumentos para a invocao na stack
        // quando descobrimos que queremos criar uma instncia desta classe. Assim, em vez de andar a
        // fazer swaps at colocarmos a instncia desta classe por baixo dos argumentos para o seu
        // construtor, chamamos este mtodo esttico para fazer esse trabalho por ns.
        // O que este mtodo  basicamente:
        // public static codegenType newInstance(args) { return new codegenType(args); }
        if (!singletonMode) {
            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "newInstance",
                    "(" + argumentsDesc + ")" + codegenType.bytecodeName(), null, null);
            mv.visitCode();
            mv.visitTypeInsn(NEW, codegenType.asmName());
            mv.visitInsn(DUP);
            int localsPos = 0;
            for (Type t : arguments) {
                mv.visitVarInsn(t.getLoadInsn(), localsPos);
                localsPos += t.getNumberSlots();
            }
            mv.visitMethodInsn(INVOKESPECIAL, codegenType.asmName(), "<init>", "(" + argumentsDesc + ")V");
            mv.visitInsn(ARETURN);
            mv.visitMaxs(0, 0);
            mv.visitEnd();
        }

        // Criar mtodos call e call_nonspeculative
        for (String[] target : new String[][] { { "call", method.name() },
                { "call_nonspeculative", method.name().replace("$speculative", "$non_speculative") } }) {
            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, target[0], "()" + Type.OBJECT.bytecodeName(), null, null);
            mv.visitCode();
            int fieldPos = 0;
            for (Type t : arguments) {
                mv.visitVarInsn(ALOAD, 0);
                mv.visitFieldInsn(GETFIELD, codegenType.asmName(), "arg" + fieldPos++, t.bytecodeName());
            }
            mv.visitMethodInsn(method.opcode() == INVOKESPECIAL ? INVOKEVIRTUAL : method.opcode(),
                    method.owner().asmName(), target[1], method.desc());
            if (method.returnType().equals(Type.PRIM_VOID)) {
                if (!jaspex.Options.FASTMODE)
                    mv.visitLdcInsn("JASPEX VOID RETURN VALUE");
                else
                    mv.visitInsn(ACONST_NULL);
            } else if (method.returnType().isPrimitive())
                boxWrap(method.returnType(), mv);
            mv.visitInsn(ARETURN);
            mv.visitMaxs(0, 0);
            mv.visitEnd();
        }

        if (singletonMode) {
            // Criar field com singleton
            cw.visitField(ACC_STATIC | ACC_FINAL | ACC_PUBLIC, "INSTANCE", codegenType.bytecodeName(), null, null);
        }

        if (Options.RVP) {
            // Field com previso
            cw.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, "PREDICTOR", CommonTypes.PREDICTOR.bytecodeName(),
                    null, null);

            { // Mtodo de acesso  previso
                MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "predict", "()" + Type.OBJECT.bytecodeName(), null,
                        null);
                mv.visitCode();
                if (method.returnType().equals(Type.PRIM_VOID)) {
                    mv.visitTypeInsn(NEW, "java/lang/AssertionError");
                    mv.visitInsn(DUP);
                    mv.visitLdcInsn("Asked for prediction for Void method");
                    mv.visitMethodInsn(INVOKESPECIAL, "java/lang/AssertionError", "<init>",
                            "(Ljava/lang/Object;)V");
                    mv.visitInsn(ATHROW);
                } else {
                    mv.visitFieldInsn(GETSTATIC, codegenType.asmName(), "PREDICTOR",
                            CommonTypes.PREDICTOR.bytecodeName());
                    mv.visitMethodInsn(INVOKEVIRTUAL, CommonTypes.PREDICTOR.asmName(), "predict",
                            "()" + Type.OBJECT.bytecodeName());
                    mv.visitInsn(ARETURN);
                }
                mv.visitMaxs(0, 0);
                mv.visitEnd();
            }
            { // Mtodo de actualizao da previso
                MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "updatePrediction",
                        "(" + Type.OBJECT.bytecodeName() + ")V", null, null);
                mv.visitCode();
                if (!method.returnType().equals(Type.PRIM_VOID)) {
                    mv.visitFieldInsn(GETSTATIC, codegenType.asmName(), "PREDICTOR",
                            CommonTypes.PREDICTOR.bytecodeName());
                    mv.visitVarInsn(ALOAD, 1);
                    mv.visitMethodInsn(INVOKEVIRTUAL, CommonTypes.PREDICTOR.asmName(), "updatePrediction",
                            "(" + Type.OBJECT.bytecodeName() + ")V");
                }
                mv.visitInsn(RETURN);
                mv.visitMaxs(0, 0);
                mv.visitEnd();
            }

        }

        // Gerar clinit caso existam fields para inicializar
        if (singletonMode || Options.RVP) {
            // Adicionar static initializer
            MethodVisitor mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
            mv.visitCode();
            if (singletonMode) {
                mv.visitTypeInsn(NEW, codegenType.asmName());
                mv.visitInsn(DUP);
                mv.visitMethodInsn(INVOKESPECIAL, codegenType.asmName(), "<init>", "()V");
                mv.visitFieldInsn(PUTSTATIC, codegenType.asmName(), "INSTANCE", codegenType.bytecodeName());
            }
            if (Options.RVP) {
                mv.visitLdcInsn(method.returnType().bytecodeName());
                mv.visitMethodInsn(INVOKESTATIC, CommonTypes.PREDICTORFACTORY.asmName(), "newPredictor",
                        "(" + Type.STRING.bytecodeName() + ")" + CommonTypes.PREDICTOR.bytecodeName());
                mv.visitFieldInsn(PUTSTATIC, codegenType.asmName(), "PREDICTOR",
                        CommonTypes.PREDICTOR.bytecodeName());
            }
            mv.visitInsn(RETURN);
            mv.visitMaxs(0, 0);
            mv.visitEnd();
        }

        // Criar toString
        {
            Type sb = Type.fromClass(StringBuilder.class);
            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "toString", "()" + Type.STRING.bytecodeName(), null,
                    null);
            mv.visitCode();
            mv.visitTypeInsn(NEW, sb.asmName());
            mv.visitInsn(DUP);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESPECIAL, Type.OBJECT.asmName(), "toString", "()" + Type.STRING.bytecodeName());
            mv.visitMethodInsn(INVOKESPECIAL, sb.asmName(), "<init>", "(" + Type.STRING.bytecodeName() + ")V");
            mv.visitVarInsn(ASTORE, 1);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitLdcInsn("{");
            mv.visitMethodInsn(INVOKEVIRTUAL, sb.asmName(), "append",
                    "(" + Type.STRING.bytecodeName() + ")" + sb.bytecodeName());
            int fieldPos = 0;
            boolean first = true;
            for (Type t : arguments) {
                mv.visitVarInsn(ALOAD, 1);
                mv.visitLdcInsn((first ? "" : ", ") + "arg" + fieldPos + ": ");
                first = false;
                mv.visitMethodInsn(INVOKEVIRTUAL, sb.asmName(), "append",
                        "(" + Type.STRING.bytecodeName() + ")" + sb.bytecodeName());
                mv.visitVarInsn(ALOAD, 1);
                mv.visitVarInsn(ALOAD, 0);
                mv.visitFieldInsn(GETFIELD, codegenType.asmName(), "arg" + fieldPos++, t.bytecodeName());
                if (t.isPrimitive())
                    boxWrap(t, mv);
                mv.visitMethodInsn(INVOKESTATIC, Type.STRING.asmName(), "valueOf",
                        "(" + Type.OBJECT.bytecodeName() + ")" + Type.STRING.bytecodeName());
                mv.visitMethodInsn(INVOKEVIRTUAL, sb.asmName(), "append",
                        "(" + Type.STRING.bytecodeName() + ")" + sb.bytecodeName());
            }
            mv.visitVarInsn(ALOAD, 1);
            mv.visitLdcInsn("} ");
            mv.visitMethodInsn(INVOKEVIRTUAL, sb.asmName(), "append",
                    "(" + Type.STRING.bytecodeName() + ")" + sb.bytecodeName());
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, Type.OBJECT.asmName(), "toString", "()" + Type.STRING.bytecodeName());
            mv.visitInsn(ARETURN);
            mv.visitMaxs(0, 0);
            mv.visitEnd();
        }

        if (Options.ALLOWDUMMYTX && SpeculationSkiplist.useDummyTransaction(method)) {
            Log.info("Setting isDummy transaction flag for " + method.owner().commonName() + "." + method.name());

            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "useDummyTransaction", "()Z", null, null);
            mv.visitCode();
            mv.visitInsn(ICONST_1);
            mv.visitInsn(IRETURN);
            mv.visitMaxs(0, 0);
            mv.visitEnd();
        }

        cw.visitEnd();

        byte[] newClass = cw.toByteArray();

        //if (speculation.SpeculativeClassLoader.PRINTCLASS) asmlib.Util.printClass(newClass);

        if (Options.WRITECLASS) {
            try {
                new java.io.File("output" + java.io.File.separatorChar).mkdir();
                java.io.FileOutputStream fos = new java.io.FileOutputStream(
                        "output" + java.io.File.separatorChar + codegenType.commonName() + ".class");
                fos.write(newClass);
                fos.close();
            } catch (java.io.FileNotFoundException e) {
                throw new Error(e);
            } catch (java.io.IOException e) {
                throw new Error(e);
            }
        }

        return newClass;
    }

    public static void boxWrap(Type argumentType, MethodVisitor mv) {
        mv.visitMethodInsn(INVOKESTATIC, argumentType.toObject().asmName(), "valueOf",
                "(" + argumentType.bytecodeName() + ")" + argumentType.toObject().bytecodeName());
    }

}