changeset 13321:e585ac5a385d

Merge.
author Chris Seaton <chris.seaton@oracle.com>
date Fri, 13 Dec 2013 14:27:03 +0000
parents 093353894575 (current diff) da0851712519 (diff)
children f28ea693056f 0393767ae0fc
files
diffstat 21 files changed, 393 insertions(+), 125 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java	Fri Dec 13 14:26:39 2013 +0000
+++ b/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java	Fri Dec 13 14:27:03 2013 +0000
@@ -1446,6 +1446,14 @@
         emitByte(0x50 | encode);
     }
 
+    public void pushfq() {
+        emitByte(0x9c);
+    }
+
+    public void popfq() {
+        emitByte(0x9D);
+    }
+
     public final void ret(int imm16) {
         if (imm16 == 0) {
             emitByte(0xC3);
@@ -2279,7 +2287,11 @@
         subq(dst, imm32, false);
     }
 
-    public final void subq(Register dst, int imm32, boolean force32Imm) {
+    public final void subqWide(Register dst, int imm32) {
+        subq(dst, imm32, true);
+    }
+
+    private void subq(Register dst, int imm32, boolean force32Imm) {
         prefixqAndEncode(dst.encoding);
         emitArith(0x81, 0xE8, dst, imm32, force32Imm);
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CommonedConstantsTest.java	Fri Dec 13 14:27:03 2013 +0000
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2012, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.compiler.test;
+
+import java.lang.reflect.*;
+
+import org.junit.*;
+
+import com.oracle.graal.api.meta.*;
+
+/**
+ * Tests any optimization that commons loads of non-inlineable constants.
+ */
+public class CommonedConstantsTest extends GraalCompilerTest {
+
+    public static final String[] array = {"1", "2", null};
+
+    // A method where a constant is used on the normal and exception edge of a non-inlined call.
+    // The dominating block of both usages is the block containing the call.
+    public static Object testSnippet(String[] arr, int i) {
+        Object result = null;
+        try {
+            result = Array.get(arr, i);
+        } catch (ArrayIndexOutOfBoundsException e) {
+            result = array[0];
+        }
+        if (result == null) {
+            result = array[2];
+        }
+        return result;
+    }
+
+    @Test
+    public void test0() {
+        // Ensure the exception path is profiled
+        ResolvedJavaMethod javaMethod = getMetaAccess().lookupJavaMethod(getMethod("testSnippet"));
+        javaMethod.reprofile();
+        testSnippet(array, array.length);
+
+        test("testSnippet", array, 0);
+        test("testSnippet", array, 2);
+        test("testSnippet", array, 3);
+        test("testSnippet", array, 1);
+    }
+}
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Fri Dec 13 14:26:39 2013 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Fri Dec 13 14:27:03 2013 +0000
@@ -262,14 +262,13 @@
             for (Block b : lir.linearScanOrder()) {
                 emitBlock(lirGen, b);
             }
+            lirGen.beforeRegisterAllocation();
 
             Debug.dump(lir, "After LIR generation");
         } catch (Throwable e) {
             throw Debug.handle(e);
         }
 
-        lirGen.beforeRegisterAllocation();
-
         try (Scope s = Debug.scope("Allocator")) {
             if (backend.shouldAllocateRegisters()) {
                 new LinearScan(target, lir, lirGen, frameMap).allocate();
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java	Fri Dec 13 14:26:39 2013 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java	Fri Dec 13 14:27:03 2013 +0000
@@ -42,6 +42,7 @@
 import com.oracle.graal.lir.StandardOp.BlockEndOp;
 import com.oracle.graal.lir.StandardOp.JumpOp;
 import com.oracle.graal.lir.StandardOp.LabelOp;
+import com.oracle.graal.lir.StandardOp.NoOp;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.PhiNode.PhiType;
 import com.oracle.graal.nodes.calc.*;
@@ -82,11 +83,69 @@
     private final boolean printIRWithLIR;
 
     /**
-     * Maps constants the variables within the scope of a single block to avoid loading a constant
+     * Maps constants to variables within the scope of a single block to avoid loading a constant
      * more than once per block.
      */
     private Map<Constant, Variable> constantsLoadedInCurrentBlock;
 
+    /**
+     * Handle for an operation that loads a constant into a variable. The operation starts in the
+     * first block where the constant is used but will eventually be
+     * {@linkplain LIRGenerator#insertConstantLoads() moved} to a block dominating all usages of the
+     * constant.
+     */
+    public static class LoadConstant implements Comparable<LoadConstant> {
+        /**
+         * The index of {@link #op} within {@link #block}'s instruction list or -1 if {@code op} is
+         * to be moved to a dominator block.
+         */
+        int index;
+
+        /**
+         * The operation that loads the constant.
+         */
+        private final LIRInstruction op;
+
+        /**
+         * The block that does or will contain {@link #op}. This is initially the block where the
+         * first usage of the constant is seen during LIR generation.
+         */
+        private Block block;
+
+        /**
+         * The variable into which the constant is loaded.
+         */
+        private final Variable variable;
+
+        public LoadConstant(Variable variable, Block block, int index, LIRInstruction op) {
+            this.variable = variable;
+            this.block = block;
+            this.index = index;
+            this.op = op;
+        }
+
+        /**
+         * Sorts {@link LoadConstant} objects according to their enclosing blocks. This is used to
+         * group loads per block in {@link LIRGenerator#insertConstantLoads()}.
+         */
+        public int compareTo(LoadConstant o) {
+            if (block.getId() < o.block.getId()) {
+                return -1;
+            }
+            if (block.getId() > o.block.getId()) {
+                return 1;
+            }
+            return 0;
+        }
+
+        @Override
+        public String toString() {
+            return block + "#" + op;
+        }
+    }
+
+    private Map<Constant, LoadConstant> constantLoads;
+
     private ValueNode currentInstruction;
     private ValueNode lastInstructionPrinted; // Debugging only
 
@@ -181,6 +240,12 @@
         return operand;
     }
 
+    /**
+     * Controls whether commoning is performed on {@linkplain #canInlineConstant(Constant)
+     * non-inlinable} constants.
+     */
+    private static final boolean CommonConstantLoads = Boolean.parseBoolean(System.getProperty("graal.commonConstantLoads", "true"));
+
     private Value getConstantOperand(ValueNode node) {
         if (!ConstantNodeRecordsUsages) {
             Constant value = node.asConstant();
@@ -189,15 +254,42 @@
                     return !node.isExternal() ? setResult(node, value) : value;
                 } else {
                     Variable loadedValue;
-                    if (constantsLoadedInCurrentBlock == null) {
-                        constantsLoadedInCurrentBlock = new HashMap<>();
-                        loadedValue = null;
+                    if (CommonConstantLoads) {
+                        if (constantLoads == null) {
+                            constantLoads = new HashMap<>();
+                        }
+                        LoadConstant load = constantLoads.get(value);
+                        if (load == null) {
+                            int index = lir.lir(currentBlock).size();
+                            // loadedValue = newVariable(value.getPlatformKind());
+                            loadedValue = emitMove(value);
+                            LIRInstruction op = lir.lir(currentBlock).get(index);
+                            constantLoads.put(value, new LoadConstant(loadedValue, currentBlock, index, op));
+                        } else {
+                            Block dominator = ControlFlowGraph.commonDominator(load.block, currentBlock);
+                            loadedValue = load.variable;
+                            if (dominator != load.block) {
+                                if (load.index >= 0) {
+                                    List<LIRInstruction> instructions = lir.lir(load.block);
+                                    instructions.set(load.index, new NoOp(null, -1));
+                                    load.index = -1;
+                                }
+                            } else {
+                                assert load.block != currentBlock || load.index < lir.lir(currentBlock).size();
+                            }
+                            load.block = dominator;
+                        }
                     } else {
-                        loadedValue = constantsLoadedInCurrentBlock.get(value);
-                    }
-                    if (loadedValue == null) {
-                        loadedValue = emitMove(value);
-                        constantsLoadedInCurrentBlock.put(value, loadedValue);
+                        if (constantsLoadedInCurrentBlock == null) {
+                            constantsLoadedInCurrentBlock = new HashMap<>();
+                            loadedValue = null;
+                        } else {
+                            loadedValue = constantsLoadedInCurrentBlock.get(value);
+                        }
+                        if (loadedValue == null) {
+                            loadedValue = emitMove(value);
+                            constantsLoadedInCurrentBlock.put(value, loadedValue);
+                        }
                     }
                     return loadedValue;
                 }
@@ -795,10 +887,74 @@
 
     @Override
     public void beforeRegisterAllocation() {
+        insertConstantLoads();
     }
 
     /**
-     * Gets an garbage vale for a given kind.
+     * Moves deferred {@linkplain LoadConstant loads} of constants into blocks dominating all usages
+     * of the constant. Any operations inserted into a block are guaranteed to be immediately prior
+     * to the first control flow instruction near the end of the block.
+     */
+    private void insertConstantLoads() {
+        if (constantLoads != null) {
+            // Remove loads where all usages are in the same block.
+            for (Iterator<Map.Entry<Constant, LoadConstant>> iter = constantLoads.entrySet().iterator(); iter.hasNext();) {
+                LoadConstant lc = iter.next().getValue();
+                if (lc.index != -1) {
+                    assert lir.lir(lc.block).get(lc.index) == lc.op;
+                    iter.remove();
+                }
+            }
+            if (constantLoads.isEmpty()) {
+                return;
+            }
+
+            // Sorting groups the loads per block.
+            LoadConstant[] groupedByBlock = constantLoads.values().toArray(new LoadConstant[constantLoads.size()]);
+            Arrays.sort(groupedByBlock);
+
+            int groupBegin = 0;
+            while (true) {
+                int groupEnd = groupBegin + 1;
+                Block block = groupedByBlock[groupBegin].block;
+                while (groupEnd < groupedByBlock.length && groupedByBlock[groupEnd].block == block) {
+                    groupEnd++;
+                }
+                int groupSize = groupEnd - groupBegin;
+
+                List<LIRInstruction> ops = lir.lir(block);
+                int lastIndex = ops.size() - 1;
+                assert ops.get(lastIndex) instanceof BlockEndOp;
+                int insertionIndex = lastIndex;
+                for (int i = Math.max(0, lastIndex - MAX_EXCEPTION_EDGE_OP_DISTANCE_FROM_END); i < lastIndex; i++) {
+                    if (getExceptionEdge(ops.get(i)) != null) {
+                        insertionIndex = i;
+                        break;
+                    }
+                }
+
+                if (groupSize == 1) {
+                    ops.add(insertionIndex, groupedByBlock[groupBegin].op);
+                } else {
+                    assert groupSize > 1;
+                    List<LIRInstruction> moves = new ArrayList<>(groupSize);
+                    for (int i = groupBegin; i < groupEnd; i++) {
+                        moves.add(groupedByBlock[i].op);
+                    }
+                    ops.addAll(insertionIndex, moves);
+                }
+
+                if (groupEnd == groupedByBlock.length) {
+                    break;
+                }
+                groupBegin = groupEnd;
+            }
+            constantLoads = null;
+        }
+    }
+
+    /**
+     * Gets a garbage value for a given kind.
      */
     protected Constant zapValueForKind(PlatformKind kind) {
         long dead = 0xDEADDEADDEADDEADL;
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackend.java	Fri Dec 13 14:26:39 2013 +0000
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackend.java	Fri Dec 13 14:27:03 2013 +0000
@@ -83,8 +83,8 @@
      * @param isVerifiedEntryPoint specifies if the code buffer is currently at the verified entry
      *            point
      */
-    protected static void emitStackOverflowCheck(CompilationResultBuilder crb, int stackShadowPages, boolean afterFrameInit, boolean isVerifiedEntryPoint) {
-        if (stackShadowPages > 0) {
+    protected static void emitStackOverflowCheck(CompilationResultBuilder crb, int pagesToBang, boolean afterFrameInit, boolean isVerifiedEntryPoint) {
+        if (pagesToBang > 0) {
 
             AMD64MacroAssembler asm = (AMD64MacroAssembler) crb.asm;
             int frameSize = crb.frameMap.frameSize();
@@ -92,7 +92,7 @@
                 int lastFramePage = frameSize / unsafe.pageSize();
                 // emit multiple stack bangs for methods with frames larger than a page
                 for (int i = 0; i <= lastFramePage; i++) {
-                    int disp = (i + stackShadowPages) * unsafe.pageSize();
+                    int disp = (i + pagesToBang) * unsafe.pageSize();
                     if (afterFrameInit) {
                         disp -= frameSize;
                     }
@@ -141,12 +141,12 @@
                 }
             } else {
                 int verifiedEntryPointOffset = asm.codeBuffer.position();
-                if (!isStub && stackShadowPages > 0) {
-                    emitStackOverflowCheck(crb, stackShadowPages, false, true);
+                if (!isStub && pagesToBang > 0) {
+                    emitStackOverflowCheck(crb, pagesToBang, false, true);
                     assert asm.codeBuffer.position() - verifiedEntryPointOffset >= PATCHED_VERIFIED_ENTRY_POINT_INSTRUCTION_SIZE;
                 }
                 if (!isStub && asm.codeBuffer.position() == verifiedEntryPointOffset) {
-                    asm.subq(rsp, frameSize, true);
+                    asm.subqWide(rsp, frameSize);
                     assert asm.codeBuffer.position() - verifiedEntryPointOffset >= PATCHED_VERIFIED_ENTRY_POINT_INSTRUCTION_SIZE;
                 } else {
                     asm.decrementq(rsp, frameSize);
@@ -278,7 +278,7 @@
      * @param installedCodeOwner see {@link Backend#emitCode}
      */
     public void emitCodeBody(ResolvedJavaMethod installedCodeOwner, CompilationResultBuilder crb, LIRGenerator lirGen) {
-        lirGen.lir.emitCode(crb);
+        crb.emit(lirGen.lir);
     }
 
     /**
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Fri Dec 13 14:26:39 2013 +0000
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Fri Dec 13 14:27:03 2013 +0000
@@ -48,7 +48,7 @@
 import com.oracle.graal.hotspot.nodes.*;
 import com.oracle.graal.hotspot.stubs.*;
 import com.oracle.graal.lir.*;
-import com.oracle.graal.lir.StandardOp.PlaceholderOp;
+import com.oracle.graal.lir.StandardOp.NoOp;
 import com.oracle.graal.lir.StandardOp.SaveRegistersOp;
 import com.oracle.graal.lir.amd64.*;
 import com.oracle.graal.lir.amd64.AMD64ControlFlow.CondMoveOp;
@@ -93,14 +93,14 @@
      */
     class SaveRbp {
 
-        final PlaceholderOp placeholder;
+        final NoOp placeholder;
 
         /**
          * The slot reserved for saving RBP.
          */
         final StackSlot reservedSlot;
 
-        public SaveRbp(PlaceholderOp placeholder) {
+        public SaveRbp(NoOp placeholder) {
             this.placeholder = placeholder;
             this.reservedSlot = frameMap.allocateSpillSlot(Kind.Long);
             assert reservedSlot.getRawOffset() == -16 : reservedSlot.getRawOffset();
@@ -172,7 +172,7 @@
 
         emitIncomingValues(params);
 
-        saveRbp = new SaveRbp(new PlaceholderOp(currentBlock, lir.lir(currentBlock).size()));
+        saveRbp = new SaveRbp(new NoOp(currentBlock, lir.lir(currentBlock).size()));
         append(saveRbp.placeholder);
 
         for (LocalNode local : graph.getNodes(LocalNode.class)) {
@@ -433,6 +433,7 @@
 
     @Override
     public void beforeRegisterAllocation() {
+        super.beforeRegisterAllocation();
         boolean hasDebugInfo = lir.hasDebugInfo();
         AllocatableValue savedRbp = saveRbp.finalize(hasDebugInfo);
         if (hasDebugInfo) {
--- a/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackend.java	Fri Dec 13 14:26:39 2013 +0000
+++ b/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackend.java	Fri Dec 13 14:27:03 2013 +0000
@@ -297,7 +297,7 @@
             }
         }
         // Prologue done, Emit code for the LIR.
-        lirGen.lir.emitCode(crb);
+        crb.emit(lirGen.lir);
         // Now that code is emitted go back and figure out what the upper Bound stack size was.
         long maxStackSize = ((HSAILAssembler) crb.asm).upperBoundStackSize();
         String spillsegStringFinal;
--- a/graal/com.oracle.graal.hotspot.ptx/src/com/oracle/graal/hotspot/ptx/PTXHotSpotBackend.java	Fri Dec 13 14:26:39 2013 +0000
+++ b/graal/com.oracle.graal.hotspot.ptx/src/com/oracle/graal/hotspot/ptx/PTXHotSpotBackend.java	Fri Dec 13 14:27:03 2013 +0000
@@ -284,7 +284,7 @@
         }
         // Emit code for the LIR
         try {
-            lirGen.lir.emitCode(crb);
+            crb.emit(lirGen.lir);
         } catch (GraalInternalError e) {
             e.printStackTrace();
             // TODO : Better error handling needs to be done once
--- a/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackend.java	Fri Dec 13 14:26:39 2013 +0000
+++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackend.java	Fri Dec 13 14:27:03 2013 +0000
@@ -81,15 +81,15 @@
      * @param afterFrameInit specifies if the stack pointer has already been adjusted to allocate
      *            the current frame
      */
-    protected static void emitStackOverflowCheck(CompilationResultBuilder crb, int stackShadowPages, boolean afterFrameInit) {
-        if (stackShadowPages > 0) {
+    protected static void emitStackOverflowCheck(CompilationResultBuilder crb, int pagesToBang, boolean afterFrameInit) {
+        if (pagesToBang > 0) {
             SPARCMacroAssembler masm = (SPARCMacroAssembler) crb.asm;
             final int frameSize = crb.frameMap.totalFrameSize();
             if (frameSize > 0) {
                 int lastFramePage = frameSize / unsafe.pageSize();
                 // emit multiple stack bangs for methods with frames larger than a page
                 for (int i = 0; i <= lastFramePage; i++) {
-                    int disp = (i + stackShadowPages) * unsafe.pageSize();
+                    int disp = (i + pagesToBang) * unsafe.pageSize();
                     if (afterFrameInit) {
                         disp -= frameSize;
                     }
@@ -124,8 +124,8 @@
             final int frameSize = crb.frameMap.totalFrameSize();
 
             SPARCMacroAssembler masm = (SPARCMacroAssembler) crb.asm;
-            if (!isStub && stackShadowPages > 0) {
-                emitStackOverflowCheck(crb, stackShadowPages, false);
+            if (!isStub && pagesToBang > 0) {
+                emitStackOverflowCheck(crb, pagesToBang, false);
             }
             new Save(sp, -frameSize, sp).emit(masm);
 
@@ -207,7 +207,7 @@
         crb.recordMark(Marks.MARK_VERIFIED_ENTRY);
 
         // Emit code for the LIR
-        lirGen.lir.emitCode(crb);
+        crb.emit(lirGen.lir);
 
         HotSpotFrameContext frameContext = (HotSpotFrameContext) crb.frameContext;
         HotSpotForeignCallsProvider foreignCalls = getProviders().getForeignCalls();
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotHostBackend.java	Fri Dec 13 14:26:39 2013 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotHostBackend.java	Fri Dec 13 14:27:03 2013 +0000
@@ -40,11 +40,11 @@
     /**
      * This will be 0 if stack banging is disabled.
      */
-    protected final int stackShadowPages;
+    protected final int pagesToBang;
 
     public HotSpotHostBackend(HotSpotGraalRuntime runtime, HotSpotProviders providers) {
         super(runtime, providers);
-        this.stackShadowPages = runtime.getConfig().useStackBanging ? runtime.getConfig().stackShadowPages : 0;
+        this.pagesToBang = runtime.getConfig().useStackBanging ? runtime.getConfig().stackShadowPages : 0;
     }
 
     @Override
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodData.java	Fri Dec 13 14:26:39 2013 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodData.java	Fri Dec 13 14:27:03 2013 +0000
@@ -397,7 +397,7 @@
 
         @Override
         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
-            return sb.append(format("count(%d)", getCounterValue(data, pos)));
+            return sb.append(format("count(%d) null_seen(%s) exception_seen(%s)", getCounterValue(data, pos), getNullSeen(data, pos), getExceptionSeen(data, pos)));
         }
     }
 
@@ -524,7 +524,9 @@
         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
             RawItemProfile<ResolvedJavaType> profile = getRawTypeProfile(data, pos);
             TriState nullSeen = getNullSeen(data, pos);
-            sb.append(format("count(%d) null_seen(%s) nonprofiled_count(%d) entries(%d)", getCounterValue(data, pos), nullSeen, getTypesNotRecordedExecutionCount(data, pos), profile.entries));
+            TriState exceptionSeen = getExceptionSeen(data, pos);
+            sb.append(format("count(%d) null_seen(%s) exception_seen(%s) nonprofiled_count(%d) entries(%d)", getCounterValue(data, pos), nullSeen, exceptionSeen,
+                            getTypesNotRecordedExecutionCount(data, pos), profile.entries));
             for (int i = 0; i < profile.entries; i++) {
                 long count = profile.counts[i];
                 sb.append(format("%n  %s (%d, %4.2f)", MetaUtil.toJavaName(profile.items[i]), count, (double) count / profile.totalCount));
--- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64ControlFlow.java	Fri Dec 13 14:26:39 2013 +0000
+++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64ControlFlow.java	Fri Dec 13 14:27:03 2013 +0000
@@ -73,12 +73,11 @@
 
         @Override
         public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
-            int sourceIndex = crb.getCurrentBlockIndex();
-            if (trueDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) {
+            if (crb.isSuccessorEdge(trueDestination)) {
                 jcc(masm, true, falseDestination);
             } else {
                 jcc(masm, false, trueDestination);
-                if (!falseDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) {
+                if (!crb.isSuccessorEdge(falseDestination)) {
                     masm.jmp(falseDestination.label());
                 }
             }
--- a/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILControlFlow.java	Fri Dec 13 14:26:39 2013 +0000
+++ b/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILControlFlow.java	Fri Dec 13 14:27:03 2013 +0000
@@ -188,14 +188,13 @@
 
         @Override
         public void emitCode(CompilationResultBuilder crb, HSAILAssembler masm) {
-            int sourceIndex = crb.getCurrentBlockIndex();
-            if (trueDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) {
+            if (crb.isSuccessorEdge(trueDestination)) {
                 HSAILCompare.emit(crb, masm, condition.negate(), x, y, z, !unordered);
                 masm.cbr(masm.nameOf(falseDestination.label()));
             } else {
                 HSAILCompare.emit(crb, masm, condition, x, y, z, unordered);
                 masm.cbr(masm.nameOf(trueDestination.label()));
-                if (!falseDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) {
+                if (!crb.isSuccessorEdge(falseDestination)) {
                     masm.jmp(falseDestination.label());
                 }
             }
--- a/graal/com.oracle.graal.lir.ptx/src/com/oracle/graal/lir/ptx/PTXControlFlow.java	Fri Dec 13 14:26:39 2013 +0000
+++ b/graal/com.oracle.graal.lir.ptx/src/com/oracle/graal/lir/ptx/PTXControlFlow.java	Fri Dec 13 14:27:03 2013 +0000
@@ -85,12 +85,11 @@
 
         @Override
         public void emitCode(CompilationResultBuilder crb, PTXMacroAssembler masm) {
-            int sourceIndex = crb.getCurrentBlockIndex();
-            if (trueDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) {
+            if (crb.isSuccessorEdge(trueDestination)) {
                 masm.bra(masm.nameOf(falseDestination.label()), predRegNum);
             } else {
                 masm.bra(masm.nameOf(trueDestination.label()));
-                if (!falseDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) {
+                if (!crb.isSuccessorEdge(falseDestination)) {
                     masm.jmp(falseDestination.label());
                 }
             }
--- a/graal/com.oracle.graal.lir.sparc/src/com/oracle/graal/lir/sparc/SPARCControlFlow.java	Fri Dec 13 14:26:39 2013 +0000
+++ b/graal/com.oracle.graal.lir.sparc/src/com/oracle/graal/lir/sparc/SPARCControlFlow.java	Fri Dec 13 14:27:03 2013 +0000
@@ -73,18 +73,17 @@
 
         @Override
         public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
-            int sourceIndex = crb.getCurrentBlockIndex();
             Label actualTarget;
             Condition actualCondition;
             boolean needJump;
-            if (trueDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) {
+            if (crb.isSuccessorEdge(trueDestination)) {
                 actualCondition = condition.negate();
                 actualTarget = falseDestination.label();
                 needJump = false;
             } else {
                 actualCondition = condition;
                 actualTarget = trueDestination.label();
-                needJump = !falseDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex);
+                needJump = !crb.isSuccessorEdge(falseDestination);
             }
             assert kind == Kind.Int || kind == Kind.Long || kind == Kind.Object;
             CC cc = kind == Kind.Int ? CC.Icc : CC.Xcc;
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIR.java	Fri Dec 13 14:26:39 2013 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIR.java	Fri Dec 13 14:27:03 2013 +0000
@@ -25,10 +25,8 @@
 import java.util.*;
 
 import com.oracle.graal.api.meta.*;
-import com.oracle.graal.debug.*;
-import com.oracle.graal.graph.*;
+import com.oracle.graal.lir.LIRInstruction.StateProcedure;
 import com.oracle.graal.lir.StandardOp.BlockEndOp;
-import com.oracle.graal.lir.asm.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.cfg.*;
 
@@ -137,46 +135,6 @@
         firstVariableNumber = num;
     }
 
-    public void emitCode(CompilationResultBuilder crb) {
-        crb.frameContext.enter(crb);
-
-        // notifyBlocksOfSuccessors();
-
-        int index = 0;
-        for (Block b : codeEmittingOrder) {
-            crb.setCurrentBlockIndex(index++);
-            emitBlock(crb, b);
-        }
-    }
-
-    private void emitBlock(CompilationResultBuilder crb, Block block) {
-        if (Debug.isDumpEnabled()) {
-            crb.blockComment(String.format("block B%d %s", block.getId(), block.getLoop()));
-        }
-
-        for (LIRInstruction op : lir(block)) {
-            if (Debug.isDumpEnabled()) {
-                crb.blockComment(String.format("%d %s", op.id(), op));
-            }
-
-            try {
-                emitOp(crb, op);
-            } catch (GraalInternalError e) {
-                throw e.addContext("lir instruction", block + "@" + op.id() + " " + op + "\n" + codeEmittingOrder);
-            }
-        }
-    }
-
-    private static void emitOp(CompilationResultBuilder crb, LIRInstruction op) {
-        try {
-            op.emitCode(crb);
-        } catch (AssertionError t) {
-            throw new GraalInternalError(t);
-        } catch (RuntimeException t) {
-            throw new GraalInternalError(t);
-        }
-    }
-
     public void setHasArgInCallerFrame() {
         hasArgInCallerFrame = true;
     }
@@ -189,15 +147,55 @@
         return hasArgInCallerFrame;
     }
 
+    /**
+     * Gets the exception edge (if any) originating at a given operation.
+     */
+    public static LabelRef getExceptionEdge(LIRInstruction op) {
+        final LabelRef[] exceptionEdge = {null};
+        op.forEachState(new StateProcedure() {
+            @Override
+            protected void doState(LIRFrameState state) {
+                if (state.exceptionEdge != null) {
+                    assert exceptionEdge[0] == null;
+                    exceptionEdge[0] = state.exceptionEdge;
+                }
+            }
+        });
+        return exceptionEdge[0];
+    }
+
+    /**
+     * The maximum distance an operation with an {@linkplain #getExceptionEdge(LIRInstruction)
+     * exception edge} can be from the last instruction of a LIR block. The value of 2 is based on a
+     * non-void call operation that has an exception edge. Such a call op will have a move op after
+     * it to put the return value into the result variable.
+     * <p>
+     * The rationale for such a constant is to limit the search for an insertion point when adding
+     * move operations at the end of a block. Such moves must be inserted before all control flow
+     * instructions.
+     */
+    public static final int MAX_EXCEPTION_EDGE_OP_DISTANCE_FROM_END = 2;
+
     public static boolean verifyBlock(LIR lir, Block block) {
         List<LIRInstruction> ops = lir.lir(block);
         if (ops.size() == 0) {
-            return true;
+            return false;
         }
-        for (LIRInstruction op : ops.subList(0, ops.size() - 1)) {
+        LIRInstruction opWithExceptionEdge = null;
+        int index = 0;
+        int lastIndex = ops.size() - 1;
+        for (LIRInstruction op : ops.subList(0, lastIndex)) {
             assert !(op instanceof BlockEndOp) : op.getClass();
+            LabelRef exceptionEdge = getExceptionEdge(op);
+            if (exceptionEdge != null) {
+                assert opWithExceptionEdge == null : "multiple ops with an exception edge not allowed";
+                opWithExceptionEdge = op;
+                int distanceFromEnd = lastIndex - index;
+                assert distanceFromEnd <= MAX_EXCEPTION_EDGE_OP_DISTANCE_FROM_END;
+            }
+            index++;
         }
-        LIRInstruction end = ops.get(ops.size() - 1);
+        LIRInstruction end = ops.get(lastIndex);
         assert end instanceof BlockEndOp : end.getClass();
         return true;
     }
@@ -210,7 +208,9 @@
             for (Block pred : block.getPredecessors()) {
                 assert blocks.contains(pred) : "missing predecessor from: " + block + "to: " + pred;
             }
-            verifyBlock(lir, block);
+            if (!verifyBlock(lir, block)) {
+                return false;
+            }
         }
         return true;
     }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LabelRef.java	Fri Dec 13 14:26:39 2013 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LabelRef.java	Fri Dec 13 14:27:03 2013 +0000
@@ -22,8 +22,6 @@
  */
 package com.oracle.graal.lir;
 
-import java.util.*;
-
 import com.oracle.graal.asm.*;
 import com.oracle.graal.lir.StandardOp.BranchOp;
 import com.oracle.graal.lir.StandardOp.JumpOp;
@@ -78,21 +76,8 @@
         return ((StandardOp.LabelOp) lir.lir(getTargetBlock()).get(0)).getLabel();
     }
 
-    /**
-     * Determines if the edge represented by this object is from a block to its lexical successor in
-     * the code emitting order of blocks.
-     * 
-     * @param sourceIndex the index of this edge's {@linkplain #getSourceBlock() source} in the code
-     *            emitting order
-     */
-    public boolean isCodeEmittingOrderSuccessorEdge(int sourceIndex) {
-        List<Block> order = lir.codeEmittingOrder();
-        assert order.get(sourceIndex) == block;
-        return sourceIndex < order.size() - 1 && order.get(sourceIndex + 1) == getTargetBlock();
-    }
-
     @Override
     public String toString() {
-        return suxIndex < block.getSuccessorCount() ? getTargetBlock().toString() : "?" + block + ":" + suxIndex + "?";
+        return getSourceBlock() + " -> " + (suxIndex < block.getSuccessors().size() ? getTargetBlock() : "?");
     }
 }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/StandardOp.java	Fri Dec 13 14:26:39 2013 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/StandardOp.java	Fri Dec 13 14:27:03 2013 +0000
@@ -113,7 +113,7 @@
 
         @Override
         public void emitCode(CompilationResultBuilder crb) {
-            if (!destination.isCodeEmittingOrderSuccessorEdge(crb.getCurrentBlockIndex())) {
+            if (!crb.isSuccessorEdge(destination)) {
                 crb.asm.jmp(destination.label());
             }
         }
@@ -174,9 +174,10 @@
     }
 
     /**
-     * Placeholder for a LIR instruction that will be subsequently replaced.
+     * A LIR operation that does nothing. If the operation records its position, it can be
+     * subsequently {@linkplain #replace(LIR, LIRInstruction) replaced}.
      */
-    public static class PlaceholderOp extends LIRInstruction {
+    public static class NoOp extends LIRInstruction {
 
         /**
          * The block in which this instruction is located.
@@ -188,7 +189,7 @@
          */
         final int index;
 
-        public PlaceholderOp(Block block, int index) {
+        public NoOp(Block block, int index) {
             this.block = block;
             this.index = index;
         }
@@ -199,7 +200,9 @@
 
         @Override
         public void emitCode(CompilationResultBuilder crb) {
-            throw new GraalInternalError(this + " should have been replaced");
+            if (block != null) {
+                throw new GraalInternalError(this + " should have been replaced");
+            }
         }
     }
 }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/SwitchStrategy.java	Fri Dec 13 14:26:39 2013 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/SwitchStrategy.java	Fri Dec 13 14:27:03 2013 +0000
@@ -116,9 +116,9 @@
         }
 
         public void conditionalJumpOrDefault(int index, Condition condition, boolean canFallThrough) {
-            if (canFallThrough && defaultTarget.isCodeEmittingOrderSuccessorEdge(crb.getCurrentBlockIndex())) {
+            if (canFallThrough && crb.isSuccessorEdge(defaultTarget)) {
                 conditionalJump(index, condition, keyTargets[index].label());
-            } else if (canFallThrough && keyTargets[index].isCodeEmittingOrderSuccessorEdge(crb.getCurrentBlockIndex())) {
+            } else if (canFallThrough && crb.isSuccessorEdge(keyTargets[index])) {
                 conditionalJump(index, condition.negate(), defaultTarget.label());
             } else {
                 conditionalJump(index, condition, keyTargets[index].label());
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/asm/CompilationResultBuilder.java	Fri Dec 13 14:26:39 2013 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/asm/CompilationResultBuilder.java	Fri Dec 13 14:27:03 2013 +0000
@@ -32,6 +32,7 @@
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.lir.*;
+import com.oracle.graal.nodes.cfg.*;
 
 /**
  * Fills in a {@link CompilationResult} as its code is being assembled.
@@ -59,7 +60,12 @@
     public final FrameMap frameMap;
 
     /**
-     * The index of the block currently being processed in the code emitting block order.
+     * The LIR for which code is being generated.
+     */
+    private LIR lir;
+
+    /**
+     * The index of the block currently being emitted.
      */
     private int currentBlockIndex;
 
@@ -285,16 +291,58 @@
     }
 
     /**
-     * Gets the index of the block currently being processed in the code emitting block order.
+     * Determines if a given edge from the block currently being emitted goes to its lexical
+     * successor.
      */
-    public int getCurrentBlockIndex() {
-        return currentBlockIndex;
+    public boolean isSuccessorEdge(LabelRef edge) {
+        assert lir != null;
+        List<Block> order = lir.codeEmittingOrder();
+        assert order.get(currentBlockIndex) == edge.getSourceBlock();
+        return currentBlockIndex < order.size() - 1 && order.get(currentBlockIndex + 1) == edge.getTargetBlock();
     }
 
     /**
-     * Sets the index of the block currently being processed in the code emitting block order.
+     * Emits code for {@code lir} in its {@linkplain LIR#codeEmittingOrder() code emitting order}.
      */
-    public void setCurrentBlockIndex(int index) {
-        this.currentBlockIndex = index;
+    public void emit(@SuppressWarnings("hiding") LIR lir) {
+        assert this.lir == null;
+        assert currentBlockIndex == 0;
+        this.lir = lir;
+        this.currentBlockIndex = 0;
+        frameContext.enter(this);
+        for (Block b : lir.codeEmittingOrder()) {
+            emitBlock(b);
+            currentBlockIndex++;
+        }
+        this.lir = null;
+        this.currentBlockIndex = 0;
+    }
+
+    private void emitBlock(Block block) {
+        if (Debug.isDumpEnabled()) {
+            blockComment(String.format("block B%d %s", block.getId(), block.getLoop()));
+        }
+
+        for (LIRInstruction op : lir.lir(block)) {
+            if (Debug.isDumpEnabled()) {
+                blockComment(String.format("%d %s", op.id(), op));
+            }
+
+            try {
+                emitOp(this, op);
+            } catch (GraalInternalError e) {
+                throw e.addContext("lir instruction", block + "@" + op.id() + " " + op + "\n" + lir.codeEmittingOrder());
+            }
+        }
+    }
+
+    private static void emitOp(CompilationResultBuilder crb, LIRInstruction op) {
+        try {
+            op.emitCode(crb);
+        } catch (AssertionError t) {
+            throw new GraalInternalError(t);
+        } catch (RuntimeException t) {
+            throw new GraalInternalError(t);
+        }
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/LIRGeneratorTool.java	Fri Dec 13 14:26:39 2013 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/LIRGeneratorTool.java	Fri Dec 13 14:27:03 2013 +0000
@@ -102,6 +102,7 @@
 
     /**
      * Called just before register allocation is performed on the LIR owned by this generator.
+     * Overriding implementations of this method must call the overridden method.
      */
     void beforeRegisterAllocation();