/** Performs peephole optimizations on a program's live methods. */ private static void peephole(final BloatContext context) { final Set liveMethods = new TreeSet(new MemberRefComparator()); final CallGraph cg = context.getCallGraph(); liveMethods.addAll(cg.liveMethods()); // Perform peephole optimizations. We do this separately because // some peephole optimizations do things to the stack that // inlining doesn't like. For instance, a peephole optimizations // might make it so that a method has a non-empty stack upon // return. Inlining will barf at the sight of this. BloatBenchmark.tr("Performing peephole optimizations"); final Iterator iter = liveMethods.iterator(); while (BloatBenchmark.PEEPHOLE && iter.hasNext()) { try { final MethodEditor live = context.editMethod((MemberRef) iter.next()); Peephole.transform(live); context.commit(live.methodInfo()); context.release(live.methodInfo()); } catch (final NoSuchMethodException ex314) { BloatBenchmark.err.println("** Could not find method " + ex314.getMessage()); ex314.printStackTrace(System.err); System.exit(1); } } }
/** * Returns the live methods of a program whose root methods are the <tt>main</tt> method of a set * of classes. * * @param classes Names of classes containing root methods * @param context Repository for accessing BLOAT stuff * @return The <tt>MemberRef</tt>s of the live methods */ private static Collection liveMethods(final Collection classes, final BloatContext context) { // Determine the roots of the call graph final Set roots = new HashSet(); Iterator iter = classes.iterator(); while (iter.hasNext()) { final String className = (String) iter.next(); try { final ClassEditor ce = context.editClass(className); final MethodInfo[] methods = ce.methods(); for (int i = 0; i < methods.length; i++) { final MethodEditor me = context.editMethod(methods[i]); if (!me.name().equals("main")) { continue; } BloatBenchmark.tr(" Root " + ce.name() + "." + me.name() + me.type()); roots.add(me.memberRef()); } } catch (final ClassNotFoundException ex1) { BloatBenchmark.err.println("** Could not find class: " + ex1.getMessage()); System.exit(1); } } if (roots.isEmpty()) { BloatBenchmark.err.print("** No main method found in classes: "); iter = classes.iterator(); while (iter.hasNext()) { final String name = (String) iter.next(); BloatBenchmark.err.print(name); if (iter.hasNext()) { BloatBenchmark.err.print(", "); } } BloatBenchmark.err.println(""); } context.setRootMethods(roots); final CallGraph cg = context.getCallGraph(); final Set liveMethods = new TreeSet(new MemberRefComparator()); liveMethods.addAll(cg.liveMethods()); return (liveMethods); }
/** Specializes the live methods in a program. */ private static void specialize(final BloatContext context) { final CallGraph cg = context.getCallGraph(); final Set liveMethods = new TreeSet(new MemberRefComparator()); liveMethods.addAll(cg.liveMethods()); // Specialize all possible methods final InlineStats stats = context.getInlineStats(); if (BloatBenchmark.statsFile != null) { Specialize.STATS = true; stats.setConfigName("BloatBenchmark"); } if (BloatBenchmark.MORPH != -1) { Specialize.MAX_MORPH = BloatBenchmark.MORPH; } final Specialize spec = new Specialize(context); if (Specialize.STATS) { stats.noteLiveMethods(liveMethods.size()); stats.noteLiveClasses(cg.liveClasses().size()); } BloatBenchmark.tr("Specializing live methods"); final Iterator iter = liveMethods.iterator(); for (int count = 0; iter.hasNext(); count++) { try { final MethodEditor live = context.editMethod((MemberRef) iter.next()); if (context.ignoreMethod(live.memberRef())) { // Don't display ignored methods, it's misleading. continue; } BloatBenchmark.tr( " " + count + ") " + live.declaringClass().name() + "." + live.name() + live.type()); spec.specialize(live); } catch (final NoSuchMethodException ex2) { BloatBenchmark.err.println("** Could not find method " + ex2.getMessage()); System.exit(1); } } }
/** Inlines calls to static methods in the live methods of a given program. */ private static void inline(final BloatContext context) { final Set liveMethods = new TreeSet(new MemberRefComparator()); final CallGraph cg = context.getCallGraph(); liveMethods.addAll(cg.liveMethods()); BloatBenchmark.tr("Inlining " + liveMethods.size() + " live methods"); if (BloatBenchmark.CALLEE_SIZE != -1) { Inline.CALLEE_SIZE = BloatBenchmark.CALLEE_SIZE; } final Iterator iter = liveMethods.iterator(); for (int count = 0; BloatBenchmark.INLINE && iter.hasNext(); count++) { try { final MethodEditor live = context.editMethod((MemberRef) iter.next()); if (context.ignoreMethod(live.memberRef())) { // Don't display ignored methods, it's misleading. continue; } BloatBenchmark.tr( " " + count + ") " + live.declaringClass().name() + "." + live.name() + live.type()); final Inline inline = new Inline(context, BloatBenchmark.SIZE); inline.setMaxCallDepth(BloatBenchmark.DEPTH); inline.inline(live); // Commit here in an attempt to conserve memory context.commit(live.methodInfo()); context.release(live.methodInfo()); } catch (final NoSuchMethodException ex3) { BloatBenchmark.err.println("** Could not find method " + ex3.getMessage()); System.exit(1); } } }
/** * Inserts residency/update/swizzle checks into a method. Iterates over the bytecodes in the * method and inserts the appropriate residency opcode. * * @param method The method to which to add checks. * @see MethodEditor#code */ private static void transform(final MethodEditor method) { if (Main.VERBOSE > 1) { System.out.println("Decorating method " + method); } // Optimize initialization of arrays to speed things up. CompactArrayInitializer.transform(method); final ListIterator iter = method.code().listIterator(); // Go through the code (Instructions and Labels) in the method INST: while (iter.hasNext()) { final Object ce = iter.next(); if (Main.VERBOSE > 2) { System.out.println("Examining " + ce); } if (ce instanceof Instruction) { final Instruction inst = (Instruction) ce; int uctype = Main.NONE; // Type of update check (POINTER or // SCALAR) boolean insert_sc = false; // Insert swizzle check (aaload // only)? final int opc = inst.opcodeClass(); int depth; switch (opc) { case opcx_arraylength: case opcx_athrow: case opcx_getfield: case opcx_instanceof: { depth = 0; break; } case opcx_iaload: case opcx_laload: case opcx_faload: case opcx_daload: case opcx_baload: case opcx_caload: case opcx_saload: { depth = 1; break; } case opcx_aaload: { depth = 1; insert_sc = true; break; } case opcx_iastore: case opcx_fastore: case opcx_aastore: case opcx_bastore: case opcx_castore: case opcx_sastore: { depth = 2; break; } case opcx_lastore: case opcx_dastore: { depth = 3; break; } case opcx_putfield: { final MemberRef ref = (MemberRef) inst.operand(); depth = ref.type().stackHeight(); if (ref.type().isReference()) { uctype = Main.POINTER; } else { uctype = Main.SCALAR; } break; } case opcx_invokevirtual: case opcx_invokespecial: case opcx_invokeinterface: { final MemberRef ref = (MemberRef) inst.operand(); depth = ref.type().stackHeight(); break; } case opcx_rc: { // Skip any existing residency checks. iter.remove(); continue INST; } case opcx_aupdate: { // Skip any existing update checks. iter.remove(); continue INST; } case opcx_supdate: { // Skip any existing update checks. iter.remove(); continue INST; } default: { continue INST; } } Instruction addInst; // Insert a residency check... if (Main.RC) { Object t; // ////////////////////////////////// // Before... // +-----+------+-----------+ // | ... | inst | afterInst | // +-----+------+-----------+ // ^prev ^next // // After... // +-----+----+------+-----------+ // | ... | RC | inst | afterInst | // +-----+----+------+-----------+ // ^prev ^next // ////////////////////////////////// // +-----+------+-----------+ // | ... | inst | afterInst | // +-----+------+-----------+ // ^prev ^next t = iter.previous(); Assert.isTrue(t == inst, t + " != " + inst); // +-----+------+-----------+ // | ... | inst | afterInst | // +-----+------+-----------+ // ^prev ^next addInst = new Instruction(Opcode.opcx_rc, new Integer(depth)); iter.add(addInst); // +-----+----+------+-----------+ // | ... | RC | inst | afterInst | // +-----+----+------+-----------+ // ^prev ^next t = iter.previous(); Assert.isTrue(t == addInst, t + " != " + addInst); // +-----+----+------+-----------+ // | ... | RC | inst | afterInst | // +-----+----+------+-----------+ // ^prev ^next t = iter.next(); Assert.isTrue(t == addInst, t + " != " + addInst); // +-----+----+------+-----------+ // | ... | RC | inst | afterInst | // +-----+----+------+-----------+ // ^prev ^next t = iter.next(); Assert.isTrue(t == inst, t + " != " + inst); // +-----+----+------+-----------+ // | ... | RC | inst | afterInst | // +-----+----+------+-----------+ // ^prev ^next if (Main.VERBOSE > 2) { System.out.println("Inserting " + addInst + " before " + inst); } } else { if (Main.VERBOSE > 2) { System.out.println("Not inserting rc before " + inst); } } // Insert a swizzle check... if (insert_sc) { if (Main.SC) { Object t; // //////////////////////////////////////////// // Before... // +-----+------+-----------+ // | ... | inst | afterInst | // +-----+------+-----------+ // ^prev ^next // // After... // +-----+------+----------+------+-----------+ // | ... | dup2 | aswizzle | inst | afterInst | // +-----+------+----------+------+-----------+ // ^prev ^next // ///////////////////////////////////////////// // +-----+------+-----------+ // | ... | inst | afterInst | // +-----+------+-----------+ // ^prev ^next t = iter.previous(); Assert.isTrue(t == inst, t + " != " + inst); // +-----+------+-----------+ // | ... | inst | afterInst | // +-----+------+-----------+ // ^prev ^next addInst = new Instruction(Opcode.opcx_dup2); iter.add(addInst); // +-----+------+------+-----------+ // | ... | dup2 | inst | afterInst | // +-----+------+------+-----------+ // ^prev ^next t = iter.previous(); Assert.isTrue(t == addInst, t + " != " + addInst); // +-----+------+------+-----------+ // | ... | dup2 | inst | afterInst | // +-----+------+------+-----------+ // ^prev ^next t = iter.next(); Assert.isTrue(t == addInst, t + " != " + addInst); // +-----+------+------+-----------+ // | ... | dup2 | inst | afterInst | // +-----+------+------+-----------+ // ^prev ^next addInst = new Instruction(Opcode.opcx_aswizzle); iter.add(addInst); // +-----+------+----------+------+-----------+ // | ... | dup2 | aswizzle | inst | afterInst | // +-----+------+----------+------+-----------+ // ^prev ^next t = iter.previous(); Assert.isTrue(t == addInst, t + " != " + addInst); // +-----+------+----------+------+-----------+ // | ... | dup2 | aswizzle | inst | afterInst | // +-----+------+----------+------+-----------+ // ^prev ^next t = iter.next(); Assert.isTrue(t == addInst, t + " != " + addInst); // +-----+------+----------+------+-----------+ // | ... | dup2 | aswizzle | inst | afterInst | // +-----+------+----------+------+-----------+ // ^prev ^next t = iter.next(); Assert.isTrue(t == inst, t + " != " + inst); // +-----+------+----------+------+-----------+ // | ... | dup2 | aswizzle | inst | afterInst | // +-----+------+----------+------+-----------+ // ^prev ^next if (Main.VERBOSE > 2) { System.out.println("Inserting dup2,aswizzle before " + inst); } } else { if (Main.VERBOSE > 2) { System.out.println("Not inserting aswizzle before " + inst); } } } // Insert an update check... if (uctype != Main.NONE) { if (Main.UC) { Object t; // //////////////////////////////////////////// // Before... // +-----+------+-----------+ // | ... | inst | afterInst | // +-----+------+-----------+ // ^prev ^next // // After... // +-----+---------+------+-----------+ // | ... | aupdate | inst | afterInst | // +-----+---------+------+-----------+ // ^prev ^next // ///////////////////////////////////////////// // +-----+------+-----------+ // | ... | inst | afterInst | // +-----+------+-----------+ // ^prev ^next t = iter.previous(); Assert.isTrue(t == inst, t + " != " + inst); // +-----+------+-----------+ // | ... | inst | afterInst | // +-----+------+-----------+ // ^prev ^next addInst = new Instruction(Opcode.opcx_aupdate, new Integer(depth)); /* * if (uctype == POINTER) { addInst = new * Instruction(opcx_aupdate, new Integer(depth)); } else { * addInst = new Instruction(opcx_supdate, new * Integer(depth)); } */ iter.add(addInst); // +-----+---------+------+-----------+ // | ... | aupdate | inst | afterInst | // +-----+---------+------+-----------+ // ^prev ^next t = iter.previous(); Assert.isTrue(t == addInst, t + " != " + addInst); // +-----+---------+------+-----------+ // | ... | aupdate | inst | afterInst | // +-----+---------+------+-----------+ // ^prev ^next t = iter.next(); Assert.isTrue(t == addInst, t + " != " + addInst); // +-----+---------+------+-----------+ // | ... | aupdate | inst | afterInst | // +-----+---------+------+-----------+ // ^prev ^next t = iter.next(); Assert.isTrue(t == inst, t + " != " + inst); // +-----+---------+------+-----------+ // | ... | aupdate | inst | afterInst | // +-----+---------+------+-----------+ // ^prev ^next if (Main.VERBOSE > 2) { System.out.println("Inserting " + addInst + " before " + inst); } } else if (Main.VERBOSE > 2) { System.out.println("Not inserting uc before " + inst); } } } } }
/** * Performs intraprocedural BLOAT on a program's live methods. * * @param liveMethods Should be alphabetized. This way we can commit a class once we've BLOATed * all of its methods. */ private static void intraBloat(final Collection liveMethods, final BloatContext context) { ClassEditor prevClass = null; final Iterator iter = liveMethods.iterator(); for (int count = 0; iter.hasNext(); count++) { MethodEditor live = null; ClassEditor ce = null; // Hack to make sure commit happens try { live = context.editMethod((MemberRef) iter.next()); ce = context.editClass(live.declaringClass().classInfo()); } catch (final NoSuchMethodException ex3) { BloatBenchmark.err.println("** Could not find method " + ex3.getMessage()); System.exit(1); } /* So we can skip classes or packages */ final String name = ce.type().className(); final String qual = ce.type().qualifier() + "/*"; boolean skip = false; for (int i = 0; i < BloatBenchmark.SKIP.size(); i++) { final String pkg = (String) BloatBenchmark.SKIP.get(i); if (name.equals(pkg) || qual.equals(pkg)) { skip = true; break; } } if (context.ignoreMethod(live.memberRef()) || skip) { // Don't display ignored methods, it's misleading. context.release(live.methodInfo()); continue; } final Runtime runtime = Runtime.getRuntime(); runtime.gc(); final Date start = new Date(); BloatBenchmark.tr( " " + count + ") " + live.declaringClass().name() + "." + live.name() + live.type()); BloatBenchmark.tr(" Start: " + start); try { EDU.purdue.cs.bloat.optimize.Main.TRACE = BloatBenchmark.TRACE; if (!BloatBenchmark.VERIFY) { EDU.purdue.cs.bloat.optimize.Main.VERIFY = false; } EDU.purdue.cs.bloat.optimize.Main.bloatMethod(live, context); } catch (final Exception oops) { BloatBenchmark.err.println("******************************************"); BloatBenchmark.err.println( "Exception while BLOATing " + live.declaringClass().name() + "." + live.name() + live.type()); BloatBenchmark.err.println(oops.getMessage()); oops.printStackTrace(System.err); BloatBenchmark.err.println("******************************************"); } // Commit here in an attempt to conserve memory context.commit(live.methodInfo()); context.release(live.methodInfo()); if (prevClass == null) { prevClass = ce; } else if (!prevClass.equals(ce)) { // We've finished BLOATed the methods for prevClass, commit // prevClass and move on BloatBenchmark.tr(prevClass.type() + " != " + ce.type()); context.commit(prevClass.classInfo()); context.release(prevClass.classInfo()); // context.commitDirty(); // tr(context.toString()); prevClass = ce; } else { context.release(ce.classInfo()); } final Date end = new Date(); BloatBenchmark.tr(" Ellapsed time: " + (end.getTime() - start.getTime()) + " ms"); } context.commitDirty(); }