changeset 9572:d6bf345d58c1

Merge
author Gilles Duboscq <duboscq@ssw.jku.at>
date Mon, 06 May 2013 15:25:33 +0200
parents f0ef8f58a1d9 (current diff) f64a3fec4e42 (diff)
children 5cf60de9d7d2 c064c48b9387
files
diffstat 29 files changed, 491 insertions(+), 261 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java	Mon May 06 15:25:18 2013 +0200
+++ b/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java	Mon May 06 15:25:33 2013 +0200
@@ -513,6 +513,13 @@
         emitOperandHelper(dst, src);
     }
 
+    public final void cmpl(AMD64Address dst, int imm32) {
+        prefix(dst);
+        emitByte(0x81);
+        emitOperandHelper(7, dst);
+        emitInt(imm32);
+    }
+
     // The 32-bit cmpxchg compares the value at adr with the contents of X86.rax,
     // and stores reg into adr if so; otherwise, the value at adr is loaded into X86.rax,.
     // The ZF is set if the compared values were equal, and cleared otherwise.
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotDeoptimizeCallerOp.java	Mon May 06 15:25:18 2013 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotDeoptimizeCallerOp.java	Mon May 06 15:25:33 2013 +0200
@@ -22,8 +22,6 @@
  */
 package com.oracle.graal.hotspot.amd64;
 
-import static com.oracle.graal.amd64.AMD64.*;
-import static com.oracle.graal.api.code.ValueUtil.*;
 import static com.oracle.graal.hotspot.HotSpotBackend.*;
 import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
 
@@ -51,18 +49,8 @@
 
     @Override
     public void emitCode(TargetMethodAssembler tasm, AMD64MacroAssembler masm) {
-        if (isStackSlot(savedRbp)) {
-            // Restoring RBP from the stack must be done before the frame is removed
-            masm.movq(rbp, (AMD64Address) tasm.asAddress(savedRbp));
-        } else {
-            Register framePointer = asRegister(savedRbp);
-            if (framePointer != rbp) {
-                masm.movq(rbp, framePointer);
-            }
-        }
-        if (tasm.frameContext != null) {
-            tasm.frameContext.leave(tasm);
-        }
+        leaveFrameAndRestoreRbp(tasm, masm);
+
         HotSpotGraalRuntime runtime = graalRuntime();
         Register thread = runtime.getRuntime().threadRegister();
         masm.movl(new AMD64Address(thread, runtime.getConfig().pendingDeoptimizationOffset), tasm.runtime.encodeDeoptActionAndReason(action, reason));
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotEpilogueOp.java	Mon May 06 15:25:18 2013 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotEpilogueOp.java	Mon May 06 15:25:33 2013 +0200
@@ -22,11 +22,16 @@
  */
 package com.oracle.graal.hotspot.amd64;
 
+import static com.oracle.graal.amd64.AMD64.*;
+import static com.oracle.graal.api.code.ValueUtil.*;
 import static com.oracle.graal.lir.LIRInstruction.OperandFlag.*;
 
+import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.asm.amd64.*;
 import com.oracle.graal.lir.*;
 import com.oracle.graal.lir.amd64.*;
+import com.oracle.graal.lir.asm.*;
 
 /**
  * Superclass for operations that use the value of RBP saved in a method's prologue.
@@ -41,4 +46,19 @@
     private static final Variable PLACEHOLDER = new Variable(Kind.Long, Integer.MAX_VALUE);
 
     @Use({REG, STACK}) protected AllocatableValue savedRbp = PLACEHOLDER;
+
+    protected void leaveFrameAndRestoreRbp(TargetMethodAssembler tasm, AMD64MacroAssembler masm) {
+        if (isStackSlot(savedRbp)) {
+            // Restoring RBP from the stack must be done before the frame is removed
+            masm.movq(rbp, (AMD64Address) tasm.asAddress(savedRbp));
+        } else {
+            Register framePointer = asRegister(savedRbp);
+            if (framePointer != rbp) {
+                masm.movq(rbp, framePointer);
+            }
+        }
+        if (tasm.frameContext != null) {
+            tasm.frameContext.leave(tasm);
+        }
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotJumpToExceptionHandlerInCallerOp.java	Mon May 06 15:25:33 2013 +0200
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 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.hotspot.amd64;
+
+import static com.oracle.graal.amd64.AMD64.*;
+import static com.oracle.graal.api.code.ValueUtil.*;
+import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
+import static com.oracle.graal.lir.LIRInstruction.OperandFlag.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.asm.amd64.*;
+import com.oracle.graal.asm.amd64.AMD64Assembler.ConditionFlag;
+import com.oracle.graal.lir.LIRInstruction.Opcode;
+import com.oracle.graal.lir.asm.*;
+
+/**
+ * Sets up the arguments for an exception handler in the callers frame, removes the current frame
+ * and jumps to the handler.
+ */
+@Opcode("JUMP_TO_EXCEPTION_HANDLER_IN_CALLER")
+final class AMD64HotSpotJumpToExceptionHandlerInCallerOp extends AMD64HotSpotEpilogueOp {
+
+    @Use(REG) AllocatableValue handlerInCallerPc;
+    @Use(REG) AllocatableValue exception;
+    @Use(REG) AllocatableValue exceptionPc;
+
+    AMD64HotSpotJumpToExceptionHandlerInCallerOp(AllocatableValue handlerInCallerPc, AllocatableValue exception, AllocatableValue exceptionPc) {
+        this.handlerInCallerPc = handlerInCallerPc;
+        this.exception = exception;
+        this.exceptionPc = exceptionPc;
+    }
+
+    @Override
+    public void emitCode(TargetMethodAssembler tasm, AMD64MacroAssembler masm) {
+        leaveFrameAndRestoreRbp(tasm, masm);
+
+        // Discard the return address, thus completing restoration of caller frame
+        masm.incrementq(rsp, 8);
+
+        // Restore rsp from rbp if the exception PC is a method handle call site.
+        Register thread = graalRuntime().getRuntime().threadRegister();
+        int isMethodHandleReturnOffset = graalRuntime().getConfig().threadIsMethodHandleReturnOffset;
+        AMD64Address dst = new AMD64Address(thread, isMethodHandleReturnOffset);
+        masm.cmpl(dst, 0);
+        masm.cmovq(ConditionFlag.NotEqual, rsp, rbp);
+
+        masm.jmp(asRegister(handlerInCallerPc));
+    }
+}
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Mon May 06 15:25:18 2013 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Mon May 06 15:25:33 2013 +0200
@@ -25,7 +25,7 @@
 import static com.oracle.graal.amd64.AMD64.*;
 import static com.oracle.graal.api.code.CallingConvention.Type.*;
 import static com.oracle.graal.api.code.ValueUtil.*;
-import static com.oracle.graal.hotspot.amd64.AMD64HotSpotUnwindOp.*;
+import static com.oracle.graal.hotspot.HotSpotBackend.*;
 
 import java.lang.reflect.*;
 import java.util.*;
@@ -115,6 +115,14 @@
      */
     List<AMD64HotSpotEpilogueOp> epilogueOps = new ArrayList<>(2);
 
+    @Override
+    public void append(LIRInstruction op) {
+        super.append(op);
+        if (op instanceof AMD64HotSpotEpilogueOp) {
+            epilogueOps.add((AMD64HotSpotEpilogueOp) op);
+        }
+    }
+
     @SuppressWarnings("hiding")
     @Override
     protected DebugInfoBuilder createDebugInfoBuilder(NodeMap<Value> nodeOperands) {
@@ -161,9 +169,7 @@
 
     @Override
     protected void emitReturn(Value input) {
-        AMD64HotSpotReturnOp op = new AMD64HotSpotReturnOp(input);
-        epilogueOps.add(op);
-        append(op);
+        append(new AMD64HotSpotReturnOp(input));
     }
 
     @Override
@@ -333,11 +339,12 @@
 
     @Override
     public void emitUnwind(Value exception) {
-        RegisterValue exceptionParameter = EXCEPTION.asValue();
+        RuntimeCallTarget stub = getRuntime().lookupRuntimeCall(HotSpotBackend.UNWIND_EXCEPTION_TO_CALLER);
+        CallingConvention cc = stub.getCallingConvention();
+        assert cc.getArgumentCount() == 2;
+        RegisterValue exceptionParameter = (RegisterValue) cc.getArgument(0);
         emitMove(exceptionParameter, exception);
-        AMD64HotSpotUnwindOp op = new AMD64HotSpotUnwindOp(exceptionParameter);
-        epilogueOps.add(op);
-        append(op);
+        append(new AMD64HotSpotUnwindOp(exceptionParameter));
     }
 
     @Override
@@ -347,15 +354,25 @@
 
     @Override
     public void emitDeoptimizeCaller(DeoptimizationAction action, DeoptimizationReason reason) {
-        AMD64HotSpotDeoptimizeCallerOp op = new AMD64HotSpotDeoptimizeCallerOp(action, reason);
-        epilogueOps.add(op);
-        append(op);
+        append(new AMD64HotSpotDeoptimizeCallerOp(action, reason));
     }
 
     @Override
     public void emitPatchReturnAddress(ValueNode address) {
-        load(operand(address));
-        AMD64HotSpotPatchReturnAddressOp op = new AMD64HotSpotPatchReturnAddressOp(load(operand(address)));
+        append(new AMD64HotSpotPatchReturnAddressOp(load(operand(address))));
+    }
+
+    @Override
+    public void emitJumpToExceptionHandlerInCaller(ValueNode handlerInCallerPc, ValueNode exception, ValueNode exceptionPc) {
+        Variable handler = load(operand(handlerInCallerPc));
+        RuntimeCallTarget stub = getRuntime().lookupRuntimeCall(EXCEPTION_HANDLER_IN_CALLER);
+        CallingConvention cc = stub.getCallingConvention();
+        assert cc.getArgumentCount() == 2;
+        RegisterValue exceptionFixed = (RegisterValue) cc.getArgument(0);
+        RegisterValue exceptionPcFixed = (RegisterValue) cc.getArgument(1);
+        emitMove(exceptionFixed, operand(exception));
+        emitMove(exceptionPcFixed, operand(exceptionPc));
+        AMD64HotSpotJumpToExceptionHandlerInCallerOp op = new AMD64HotSpotJumpToExceptionHandlerInCallerOp(handler, exceptionFixed, exceptionPcFixed);
         append(op);
     }
 
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotReturnOp.java	Mon May 06 15:25:18 2013 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotReturnOp.java	Mon May 06 15:25:33 2013 +0200
@@ -22,11 +22,8 @@
  */
 package com.oracle.graal.hotspot.amd64;
 
-import static com.oracle.graal.amd64.AMD64.*;
-import static com.oracle.graal.api.code.ValueUtil.*;
 import static com.oracle.graal.lir.LIRInstruction.OperandFlag.*;
 
-import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.asm.amd64.*;
 import com.oracle.graal.lir.LIRInstruction.Opcode;
@@ -46,18 +43,7 @@
 
     @Override
     public void emitCode(TargetMethodAssembler tasm, AMD64MacroAssembler masm) {
-        if (isStackSlot(savedRbp)) {
-            // Restoring RBP from the stack must be done before the frame is removed
-            masm.movq(rbp, (AMD64Address) tasm.asAddress(savedRbp));
-        } else {
-            Register framePointer = asRegister(savedRbp);
-            if (framePointer != rbp) {
-                masm.movq(rbp, framePointer);
-            }
-        }
-        if (tasm.frameContext != null) {
-            tasm.frameContext.leave(tasm);
-        }
+        leaveFrameAndRestoreRbp(tasm, masm);
         masm.ret(0);
     }
 }
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRuntime.java	Mon May 06 15:25:18 2013 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRuntime.java	Mon May 06 15:25:33 2013 +0200
@@ -25,7 +25,6 @@
 import static com.oracle.graal.amd64.AMD64.*;
 import static com.oracle.graal.compiler.amd64.AMD64LIRGenerator.*;
 import static com.oracle.graal.hotspot.HotSpotBackend.*;
-import static com.oracle.graal.hotspot.amd64.AMD64HotSpotUnwindOp.*;
 import static com.oracle.graal.hotspot.nodes.MonitorEnterStubCall.*;
 import static com.oracle.graal.hotspot.nodes.MonitorExitStubCall.*;
 import static com.oracle.graal.hotspot.nodes.VMErrorNode.*;
@@ -55,15 +54,18 @@
         Kind word = graalRuntime.getTarget().wordKind;
 
         // @formatter:off
+
+        // The calling convention for the exception handler stub is (only?) defined in
+        // TemplateInterpreterGenerator::generate_throw_exception()
+        // in templateInterpreter_x86_64.cpp around line 1923 
         addStubCall(EXCEPTION_HANDLER,
-               /*             ret */ ret(Kind.Void),
+                /*            ret */ ret(Kind.Void),
                /* arg0: exception */ rax.asValue(Kind.Object),
              /* arg1: exceptionPc */ rdx.asValue(word));
 
-        addRuntimeCall(UNWIND_EXCEPTION, config.unwindExceptionStub,
-               /*           temps */ null,
-               /*             ret */ ret(Kind.Void),
-               /* arg0: exception */ rax.asValue(Kind.Object));
+        addJump(EXCEPTION_HANDLER_IN_CALLER,
+                /* arg0: exception */ rax.asValue(Kind.Object),
+               /* arg1: exceptionPc */ rdx.asValue(word));
 
         addRuntimeCall(ARITHMETIC_FREM, config.arithmeticFremStub,
                 /*           temps */ new Register[]{AMD64.rax},
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotUnwindOp.java	Mon May 06 15:25:18 2013 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotUnwindOp.java	Mon May 06 15:25:33 2013 +0200
@@ -24,53 +24,42 @@
 
 import static com.oracle.graal.amd64.AMD64.*;
 import static com.oracle.graal.api.code.ValueUtil.*;
+import static com.oracle.graal.hotspot.HotSpotBackend.*;
 import static com.oracle.graal.lir.LIRInstruction.OperandFlag.*;
 
-import com.oracle.graal.amd64.*;
 import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
-import com.oracle.graal.api.meta.*;
 import com.oracle.graal.asm.amd64.*;
+import com.oracle.graal.hotspot.*;
+import com.oracle.graal.hotspot.stubs.*;
 import com.oracle.graal.lir.LIRInstruction.Opcode;
 import com.oracle.graal.lir.amd64.*;
 import com.oracle.graal.lir.asm.*;
 
 /**
- * Performs an unwind to throw an exception.
+ * Removes the current frame and jumps to the {@link UnwindExceptionToCallerStub}.
  */
 @Opcode("UNWIND")
 final class AMD64HotSpotUnwindOp extends AMD64HotSpotEpilogueOp {
 
-    public static final Descriptor UNWIND_EXCEPTION = new Descriptor("unwindException", true, void.class, Object.class);
+    @Use({REG}) protected RegisterValue exception;
 
-    /**
-     * Unwind stub expects the exception in RAX.
-     */
-    public static final Register EXCEPTION = AMD64.rax;
-
-    @Use({REG}) protected AllocatableValue exception;
-
-    AMD64HotSpotUnwindOp(AllocatableValue exception) {
+    AMD64HotSpotUnwindOp(RegisterValue exception) {
         this.exception = exception;
-        assert asRegister(exception) == EXCEPTION;
     }
 
     @Override
     public void emitCode(TargetMethodAssembler tasm, AMD64MacroAssembler masm) {
-        // Copy the saved RBP value into the slot just below the return address
-        // so that the stub can pick it up from there.
-        AMD64Address rbpSlot;
-        int rbpSlotOffset = tasm.frameMap.frameSize() - 8;
-        if (isStackSlot(savedRbp)) {
-            rbpSlot = (AMD64Address) tasm.asAddress(savedRbp);
-            assert rbpSlot.getDisplacement() == rbpSlotOffset;
-        } else {
-            rbpSlot = new AMD64Address(rsp, rbpSlotOffset);
-            masm.movq(rbpSlot, asRegister(savedRbp));
-        }
+        leaveFrameAndRestoreRbp(tasm, masm);
 
-        // Pass the address of the RBP slot in RBP itself
-        masm.leaq(rbp, rbpSlot);
-        AMD64Call.directCall(tasm, masm, tasm.runtime.lookupRuntimeCall(UNWIND_EXCEPTION), AMD64.r10, false, null);
+        RuntimeCallTarget stub = tasm.runtime.lookupRuntimeCall(UNWIND_EXCEPTION_TO_CALLER);
+        CallingConvention cc = stub.getCallingConvention();
+        assert cc.getArgumentCount() == 2;
+        assert exception == cc.getArgument(0);
+
+        // Get return address (is on top of stack after leave).
+        Register returnAddress = asRegister(cc.getArgument(1));
+        masm.movq(returnAddress, new AMD64Address(rsp, 0));
+
+        AMD64Call.directJmp(tasm, masm, tasm.runtime.lookupRuntimeCall(HotSpotBackend.UNWIND_EXCEPTION_TO_CALLER));
     }
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotBackend.java	Mon May 06 15:25:18 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotBackend.java	Mon May 06 15:25:33 2013 +0200
@@ -28,6 +28,7 @@
 import com.oracle.graal.hotspot.bridge.*;
 import com.oracle.graal.hotspot.meta.*;
 import com.oracle.graal.hotspot.stubs.*;
+import com.oracle.graal.nodes.*;
 import com.oracle.graal.word.*;
 
 /**
@@ -56,6 +57,17 @@
      */
     public static final Descriptor IC_MISS_HANDLER = new Descriptor("icMissHandler", true, void.class);
 
+    /**
+     * Descriptor for {@link UnwindExceptionToCallerStub}. This stub is called by code generated
+     * from {@link UnwindNode}.
+     */
+    public static final Descriptor UNWIND_EXCEPTION_TO_CALLER = new Descriptor("unwindExceptionToCaller", true, void.class, Object.class, Word.class);
+
+    /**
+     * Descriptor for the arguments when unwinding to an exception handler in a caller.
+     */
+    public static final Descriptor EXCEPTION_HANDLER_IN_CALLER = new Descriptor("exceptionHandlerInCaller", false, void.class, Object.class, Word.class);
+
     public HotSpotBackend(HotSpotRuntime runtime, TargetDescription target) {
         super(runtime, target);
     }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotLIRGenerator.java	Mon May 06 15:25:18 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotLIRGenerator.java	Mon May 06 15:25:33 2013 +0200
@@ -47,6 +47,8 @@
 
     void emitPatchReturnAddress(ValueNode address);
 
+    void emitJumpToExceptionHandlerInCaller(ValueNode handlerInCallerPc, ValueNode exception, ValueNode exceptionPc);
+
     void visitDirectCompareAndSwap(DirectCompareAndSwapNode x);
 
     /**
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotRuntimeCallTarget.java	Mon May 06 15:25:18 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotRuntimeCallTarget.java	Mon May 06 15:25:33 2013 +0200
@@ -36,6 +36,11 @@
 public class HotSpotRuntimeCallTarget implements RuntimeCallTarget, InvokeTarget {
 
     /**
+     * Sentinel marker for a computed jump address.
+     */
+    public static final long JUMP_ADDRESS = 0xDEADDEADBEEFBEEFL;
+
+    /**
      * The descriptor of the stub. This is for informational purposes only.
      */
     public final Descriptor descriptor;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Mon May 06 15:25:18 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Mon May 06 15:25:33 2013 +0200
@@ -137,6 +137,9 @@
      */
     public int threadTlabEndOffset;
 
+    /**
+     * The value of JavaThread::threadObj_offset().
+     */
     public int threadObjectOffset;
 
     /**
@@ -233,6 +236,11 @@
     public int klassHasFinalizerFlag;
 
     /**
+     * The value of JavaThread::is_method_handle_return_offset().
+     */
+    public int threadIsMethodHandleReturnOffset;
+
+    /**
      * Offset of the _exception_oop field in Thread (defined in thread.hpp). This field is used to
      * pass exception objects into and out of the runtime system during exception handling for
      * compiled code.
@@ -392,7 +400,8 @@
     public long threadIsInterruptedAddress;
     public long vmMessageAddress;
     public long identityHashCodeAddress;
-    public long handleExceptionForPcAddress;
+    public long exceptionHandlerForPcAddress;
+    public long exceptionHandlerForReturnAddressAddress;
 
     public int deoptReasonNullCheck;
     public int deoptReasonRangeCheck;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java	Mon May 06 15:25:18 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java	Mon May 06 15:25:33 2013 +0200
@@ -43,6 +43,7 @@
 import static com.oracle.graal.hotspot.stubs.NewMultiArrayStub.*;
 import static com.oracle.graal.hotspot.stubs.RegisterFinalizerStub.*;
 import static com.oracle.graal.hotspot.stubs.ThreadIsInterruptedStub.*;
+import static com.oracle.graal.hotspot.stubs.UnwindExceptionToCallerStub.*;
 import static com.oracle.graal.java.GraphBuilderPhase.RuntimeCalls.*;
 import static com.oracle.graal.nodes.java.RegisterFinalizerNode.*;
 import static com.oracle.graal.replacements.Log.*;
@@ -225,10 +226,19 @@
                         /*           temps */ null,
                         /*             ret */ ret(Kind.Void));
 
-        addCRuntimeCall(EXCEPTION_HANDLER_FOR_PC, config.handleExceptionForPcAddress,
+        addCRuntimeCall(EXCEPTION_HANDLER_FOR_PC, config.exceptionHandlerForPcAddress,
                         /*             ret */ ret(word),
                         /* arg0:    thread */ nativeCallingConvention(word));
 
+        addStubCall(UNWIND_EXCEPTION_TO_CALLER,
+                        /*             ret */ ret(Kind.Void),
+                        /* arg0: exception */ javaCallingConvention(Kind.Object,
+                    /* arg1: returnAddress */                       word));
+
+        addCRuntimeCall(EXCEPTION_HANDLER_FOR_RETURN_ADDRESS, config.exceptionHandlerForReturnAddressAddress,
+                        /*             ret */ ret(word),
+                        /* arg0:    thread */ nativeCallingConvention(word,
+                    /* arg1: returnAddress */                         word));
         addStubCall(REGISTER_FINALIZER,
                         /*             ret */ ret(Kind.Void),
                         /* arg0:    object */ javaCallingConvention(Kind.Object));
@@ -374,6 +384,23 @@
         return addRuntimeCall(descriptor, 0L, null, ret, args);
     }
 
+    /**
+     * Registers the details for a jump to a target that has a signature (i.e. expects arguments in
+     * specified locations).
+     * 
+     * @param descriptor name and signature of the jump target
+     * @param args where arguments are passed to the call
+     */
+    protected RuntimeCallTarget addJump(Descriptor descriptor, AllocatableValue... args) {
+        return addRuntimeCall(descriptor, HotSpotRuntimeCallTarget.JUMP_ADDRESS, null, ret(Kind.Void), args);
+    }
+
+    /**
+     * Registers the details for a call to a runtime C/C++ function.
+     * 
+     * @param descriptor name and signature of the call
+     * @param args where arguments are passed to the call
+     */
     protected RuntimeCallTarget addCRuntimeCall(Descriptor descriptor, long address, AllocatableValue ret, AllocatableValue... args) {
         return addRuntimeCall(descriptor, address, true, null, ret, args);
     }
@@ -459,6 +486,7 @@
         registerStub(new ThreadIsInterruptedStub(this, replacements, graalRuntime.getTarget(), runtimeCalls.get(THREAD_IS_INTERRUPTED)));
         registerStub(new IdentityHashCodeStub(this, replacements, graalRuntime.getTarget(), runtimeCalls.get(IDENTITY_HASHCODE)));
         registerStub(new ExceptionHandlerStub(this, replacements, graalRuntime.getTarget(), runtimeCalls.get(EXCEPTION_HANDLER)));
+        registerStub(new UnwindExceptionToCallerStub(this, replacements, graalRuntime.getTarget(), runtimeCalls.get(UNWIND_EXCEPTION_TO_CALLER)));
     }
 
     private void registerStub(Stub stub) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/JumpToExceptionHandlerInCallerNode.java	Mon May 06 15:25:33 2013 +0200
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2009, 2011, 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.hotspot.nodes;
+
+import com.oracle.graal.hotspot.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.word.*;
+
+/**
+ * Sets up the {@linkplain HotSpotBackend#EXCEPTION_HANDLER_IN_CALLER arguments} expected by an
+ * exception handler in the caller's frame, removes the current frame and jumps to said handler.
+ */
+public class JumpToExceptionHandlerInCallerNode extends ControlSinkNode implements LIRLowerable {
+
+    @Input private ValueNode handlerInCallerPc;
+    @Input private ValueNode exception;
+    @Input private ValueNode exceptionPc;
+
+    public JumpToExceptionHandlerInCallerNode(ValueNode handlerInCallerPc, ValueNode exception, ValueNode exceptionPc) {
+        super(StampFactory.forVoid());
+        this.handlerInCallerPc = handlerInCallerPc;
+        this.exception = exception;
+        this.exceptionPc = exceptionPc;
+    }
+
+    @Override
+    public void generate(LIRGeneratorTool gen) {
+        ((HotSpotLIRGenerator) gen).emitJumpToExceptionHandlerInCaller(handlerInCallerPc, exception, exceptionPc);
+    }
+
+    @NodeIntrinsic
+    public static native void jumpToExceptionHandlerInCaller(Word handlerInCallerPc, Object exception, Word exceptionPc);
+}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotReplacementsUtil.java	Mon May 06 15:25:18 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotReplacementsUtil.java	Mon May 06 15:25:33 2013 +0200
@@ -458,14 +458,14 @@
      * Gets the value of the stack pointer register as a Word.
      */
     public static Word stackPointer() {
-        return HotSpotReplacementsUtil.registerAsWord(stackPointerRegister(), true, false);
+        return registerAsWord(stackPointerRegister(), true, false);
     }
 
     /**
      * Gets the value of the thread register as a Word.
      */
     public static Word thread() {
-        return HotSpotReplacementsUtil.registerAsWord(threadRegister(), true, false);
+        return registerAsWord(threadRegister(), true, false);
     }
 
     public static Word loadWordFromObject(Object object, int offset) {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/ExceptionHandlerStub.java	Mon May 06 15:25:18 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/ExceptionHandlerStub.java	Mon May 06 15:25:33 2013 +0200
@@ -65,7 +65,8 @@
 
     @Snippet
     private static void exceptionHandler(Object exception, Word exceptionPc) {
-        checkNoExceptionInThread(exception, exceptionPc);
+        checkNoExceptionInThread(assertionsEnabled());
+        checkExceptionNotNull(assertionsEnabled(), exception);
         writeExceptionOop(thread(), exception);
         writeExceptionPc(thread(), exceptionPc);
         if (logging()) {
@@ -85,19 +86,25 @@
         patchReturnAddress(handlerPc);
     }
 
-    private static void checkNoExceptionInThread(Object exception, Word exceptionPc) {
-        if (assertionsEnabled()) {
+    static void checkNoExceptionInThread(boolean enabled) {
+        if (enabled) {
             Object currentException = readExceptionOop(thread());
-            if (currentException != null && currentException != exception) {
-                fatal("exception oop must be null or %p, not %p", Word.fromObject(exception).rawValue(), Word.fromObject(currentException).rawValue());
+            if (currentException != null) {
+                fatal("exception object in thread must be null, not %p", Word.fromObject(currentException).rawValue());
             }
             Word currentExceptionPc = readExceptionPc(thread());
-            if (currentExceptionPc.notEqual(Word.zero()) && currentExceptionPc.notEqual(exceptionPc)) {
-                fatal("exception pc must be zero or %p, not %p", exceptionPc.rawValue(), currentExceptionPc.rawValue());
+            if (currentExceptionPc.notEqual(Word.zero())) {
+                fatal("exception PC in thread must be zero, not %p", currentExceptionPc.rawValue());
             }
         }
     }
 
+    static void checkExceptionNotNull(boolean enabled, Object exception) {
+        if (enabled && exception == null) {
+            fatal("exception must not be null");
+        }
+    }
+
     @Fold
     private static boolean logging() {
         return Boolean.getBoolean("graal.logExceptionHandlerStub");
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewArrayStub.java	Mon May 06 15:25:18 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewArrayStub.java	Mon May 06 15:25:33 2013 +0200
@@ -39,7 +39,7 @@
 import com.oracle.graal.hotspot.replacements.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.replacements.*;
-import com.oracle.graal.replacements.Snippet.ConstantParameter;
+import com.oracle.graal.replacements.Snippet.*;
 import com.oracle.graal.replacements.SnippetTemplate.Arguments;
 import com.oracle.graal.replacements.SnippetTemplate.SnippetInfo;
 import com.oracle.graal.word.*;
@@ -70,10 +70,14 @@
         args.add("hub", null);
         args.add("length", null);
         args.addConst("intArrayHub", intArrayHub);
-        args.addConst("log", Boolean.getBoolean("graal.logNewArrayStub"));
         return args;
     }
 
+    @Fold
+    private static boolean logging() {
+        return Boolean.getBoolean("graal.logNewArrayStub");
+    }
+
     /**
      * Re-attempts allocation after an initial TLAB allocation failed or was skipped (e.g., due to
      * -XX:-UseTLAB).
@@ -81,35 +85,42 @@
      * @param hub the hub of the object to be allocated
      * @param length the length of the array
      * @param intArrayHub the hub for {@code int[].class}
-     * @param log specifies if logging is enabled
      */
     @Snippet
-    private static Object newArray(Word hub, int length, @ConstantParameter Word intArrayHub, @ConstantParameter boolean log) {
+    private static Object newArray(Word hub, int length, @ConstantParameter Word intArrayHub) {
         int layoutHelper = hub.readInt(layoutHelperOffset(), FINAL_LOCATION);
         int log2ElementSize = (layoutHelper >> layoutHelperLog2ElementSizeShift()) & layoutHelperLog2ElementSizeMask();
         int headerSize = (layoutHelper >> layoutHelperHeaderSizeShift()) & layoutHelperHeaderSizeMask();
         int elementKind = (layoutHelper >> layoutHelperElementTypeShift()) & layoutHelperElementTypeMask();
         int sizeInBytes = computeArrayAllocationSize(length, wordSize(), headerSize, log2ElementSize);
-        log(log, "newArray: element kind %d\n", elementKind);
-        log(log, "newArray: array length %d\n", length);
-        log(log, "newArray: array size %d\n", sizeInBytes);
-        log(log, "newArray: hub=%p\n", hub);
+        if (logging()) {
+            printf("newArray: element kind %d\n", elementKind);
+            printf("newArray: array length %d\n", length);
+            printf("newArray: array size %d\n", sizeInBytes);
+            printf("newArray: hub=%p\n", hub.rawValue());
+        }
 
         // check that array length is small enough for fast path.
         if (length <= MAX_ARRAY_FAST_PATH_ALLOCATION_LENGTH) {
-            Word memory = refillAllocate(intArrayHub, sizeInBytes, log);
+            Word memory = refillAllocate(intArrayHub, sizeInBytes, logging());
             if (memory.notEqual(0)) {
-                log(log, "newArray: allocated new array at %p\n", memory);
+                if (logging()) {
+                    printf("newArray: allocated new array at %p\n", memory.rawValue());
+                }
                 formatArray(hub, sizeInBytes, length, headerSize, memory, Word.unsigned(arrayPrototypeMarkWord()), true);
                 return memory.toObject();
             }
         }
-        log(log, "newArray: calling new_array_c\n", 0L);
+        if (logging()) {
+            printf("newArray: calling new_array_c\n");
+        }
 
         newArrayC(NEW_ARRAY_C, thread(), hub, length);
 
         if (clearPendingException(thread())) {
-            log(log, "newArray: deoptimizing to caller\n", 0L);
+            if (logging()) {
+                printf("newArray: deoptimizing to caller\n");
+            }
             getAndClearObjectResult(thread());
             DeoptimizeCallerNode.deopt(InvalidateReprofile, RuntimeConstraint);
         }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewInstanceStub.java	Mon May 06 15:25:18 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewInstanceStub.java	Mon May 06 15:25:33 2013 +0200
@@ -71,10 +71,14 @@
         Arguments args = new Arguments(stub);
         args.add("hub", null);
         args.addConst("intArrayHub", intArrayHub);
-        args.addConst("log", Boolean.getBoolean("graal.logNewInstanceStub"));
         return args;
     }
 
+    @Fold
+    private static boolean logging() {
+        return Boolean.getBoolean("graal.logNewInstanceStub");
+    }
+
     /**
      * Re-attempts allocation after an initial TLAB allocation failed or was skipped (e.g., due to
      * -XX:-UseTLAB).
@@ -83,11 +87,11 @@
      * @param intArrayHub the hub for {@code int[].class}
      */
     @Snippet
-    private static Object newInstance(Word hub, @ConstantParameter Word intArrayHub, @ConstantParameter boolean log) {
+    private static Object newInstance(Word hub, @ConstantParameter Word intArrayHub) {
         int sizeInBytes = hub.readInt(klassInstanceSizeOffset(), FINAL_LOCATION);
         if (!forceSlowPath() && inlineContiguousAllocationSupported()) {
             if (hub.readInt(klassStateOffset(), CLASS_STATE_LOCATION) == klassStateFullyInitialized()) {
-                Word memory = refillAllocate(intArrayHub, sizeInBytes, log);
+                Word memory = refillAllocate(intArrayHub, sizeInBytes, logging());
                 if (memory.notEqual(0)) {
                     Word prototypeMarkWord = hub.readWord(prototypeMarkWordOffset(), PROTOTYPE_MARK_WORD_LOCATION);
                     initializeObjectHeader(memory, prototypeMarkWord, hub);
@@ -99,12 +103,16 @@
             }
         }
 
-        log(log, "newInstance: calling new_instance_c\n", 0L);
+        if (logging()) {
+            printf("newInstance: calling new_instance_c\n");
+        }
 
         newInstanceC(NEW_INSTANCE_C, thread(), hub);
 
         if (clearPendingException(thread())) {
-            log(log, "newInstance: deoptimizing to caller\n", 0L);
+            if (logging()) {
+                printf("newInstance: deoptimizing to caller\n");
+            }
             getAndClearObjectResult(thread());
             DeoptimizeCallerNode.deopt(InvalidateReprofile, RuntimeConstraint);
         }
@@ -134,10 +142,12 @@
         // calculate amount of free space
         Word tlabFreeSpaceInBytes = end.subtract(top);
 
-        log(log, "refillTLAB: thread=%p\n", thread);
-        log(log, "refillTLAB: top=%p\n", top);
-        log(log, "refillTLAB: end=%p\n", end);
-        log(log, "refillTLAB: tlabFreeSpaceInBytes=%d\n", tlabFreeSpaceInBytes);
+        if (log) {
+            printf("refillTLAB: thread=%p\n", thread.rawValue());
+            printf("refillTLAB: top=%p\n", top.rawValue());
+            printf("refillTLAB: end=%p\n", end.rawValue());
+            printf("refillTLAB: tlabFreeSpaceInBytes=%d\n", tlabFreeSpaceInBytes.rawValue());
+        }
 
         Word tlabFreeSpaceInWords = tlabFreeSpaceInBytes.unsignedShiftRight(log2WordSize());
 
@@ -148,10 +158,14 @@
             if (tlabStats()) {
                 // increment number of refills
                 thread.writeInt(tlabNumberOfRefillsOffset(), thread.readInt(tlabNumberOfRefillsOffset(), TLAB_NOF_REFILLS_LOCATION) + 1, TLAB_NOF_REFILLS_LOCATION);
-                log(log, "thread: %p -- number_of_refills %d\n", thread, thread.readInt(tlabNumberOfRefillsOffset(), TLAB_NOF_REFILLS_LOCATION));
+                if (log) {
+                    printf("thread: %p -- number_of_refills %d\n", thread.rawValue(), thread.readInt(tlabNumberOfRefillsOffset(), TLAB_NOF_REFILLS_LOCATION));
+                }
                 // accumulate wastage
                 Word wastage = thread.readWord(tlabFastRefillWasteOffset(), TLAB_FAST_REFILL_WASTE_LOCATION).add(tlabFreeSpaceInWords);
-                log(log, "thread: %p -- accumulated wastage %d\n", thread, wastage);
+                if (log) {
+                    printf("thread: %p -- accumulated wastage %d\n", thread.rawValue(), wastage.rawValue());
+                }
                 thread.writeWord(tlabFastRefillWasteOffset(), wastage, TLAB_FAST_REFILL_WASTE_LOCATION);
             }
 
@@ -187,7 +201,9 @@
             // Retain TLAB
             Word newRefillWasteLimit = refillWasteLimit.add(tlabRefillWasteIncrement());
             thread.writeWord(tlabRefillWasteLimitOffset(), newRefillWasteLimit, TLAB_REFILL_WASTE_LIMIT_LOCATION);
-            log(log, "refillTLAB: retaining TLAB - newRefillWasteLimit=%p\n", newRefillWasteLimit);
+            if (log) {
+                printf("refillTLAB: retaining TLAB - newRefillWasteLimit=%p\n", newRefillWasteLimit.rawValue());
+            }
 
             if (tlabStats()) {
                 thread.writeInt(tlabSlowAllocationsOffset(), thread.readInt(tlabSlowAllocationsOffset(), TLAB_SLOW_ALLOCATIONS_LOCATION) + 1, TLAB_SLOW_ALLOCATIONS_LOCATION);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/Stub.java	Mon May 06 15:25:18 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/Stub.java	Mon May 06 15:25:33 2013 +0200
@@ -232,36 +232,6 @@
         return code;
     }
 
-    static void log(boolean enabled, String message) {
-        if (enabled) {
-            printf(message);
-        }
-    }
-
-    static void log(boolean enabled, String format, long value) {
-        if (enabled) {
-            printf(format, value);
-        }
-    }
-
-    static void log(boolean enabled, String format, WordBase value) {
-        if (enabled) {
-            printf(format, value.rawValue());
-        }
-    }
-
-    static void log(boolean enabled, String format, Word v1, long v2) {
-        if (enabled) {
-            printf(format, v1.rawValue(), v2);
-        }
-    }
-
-    static void log(boolean enabled, String format, Word v1, Word v2) {
-        if (enabled) {
-            printf(format, v1.rawValue(), v2.rawValue());
-        }
-    }
-
     static void handlePendingException(boolean isObjectResult) {
         if (clearPendingException(thread())) {
             if (isObjectResult) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/UnwindExceptionToCallerStub.java	Mon May 06 15:25:33 2013 +0200
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 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.hotspot.stubs;
+
+import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
+import static com.oracle.graal.hotspot.nodes.JumpToExceptionHandlerInCallerNode.*;
+import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
+import static com.oracle.graal.hotspot.stubs.ExceptionHandlerStub.*;
+
+import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.graph.Node.ConstantNodeParameter;
+import com.oracle.graal.graph.Node.NodeIntrinsic;
+import com.oracle.graal.hotspot.*;
+import com.oracle.graal.hotspot.meta.*;
+import com.oracle.graal.hotspot.nodes.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.replacements.*;
+import com.oracle.graal.replacements.Snippet.Fold;
+import com.oracle.graal.word.*;
+
+/**
+ * Stub called by an {@link UnwindNode}. This stub executes in the frame of the method throwing an
+ * exception and completes by jumping to the exception handler in the calling frame.
+ */
+public class UnwindExceptionToCallerStub extends CRuntimeStub {
+
+    public UnwindExceptionToCallerStub(final HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotRuntimeCallTarget linkage) {
+        super(runtime, replacements, target, linkage);
+    }
+
+    /**
+     * The current frame is unwound by this stub. Therefore, it does not need to save any registers
+     * as HotSpot uses a caller save convention.
+     */
+    @Override
+    public boolean preservesRegisters() {
+        return false;
+    }
+
+    @Snippet
+    private static void unwindExceptionToCaller(Object exception, Word returnAddress) {
+        checkNoExceptionInThread(assertionsEnabled());
+        checkExceptionNotNull(assertionsEnabled(), exception);
+        if (logging()) {
+            printf("unwinding exception %p at return address %p\n", Word.fromObject(exception).rawValue(), returnAddress.rawValue());
+        }
+
+        Word handlerInCallerPc = exceptionHandlerForReturnAddress(EXCEPTION_HANDLER_FOR_RETURN_ADDRESS, thread(), returnAddress);
+
+        if (logging()) {
+            printf("handler for exception %p at return address %p is at %p\n", Word.fromObject(exception).rawValue(), returnAddress.rawValue(), handlerInCallerPc.rawValue());
+        }
+
+        jumpToExceptionHandlerInCaller(handlerInCallerPc, exception, returnAddress);
+    }
+
+    @Fold
+    private static boolean logging() {
+        return Boolean.getBoolean("graal.logUnwindExceptionToCallerStub");
+    }
+
+    @Fold
+    @SuppressWarnings("all")
+    private static boolean assertionsEnabled() {
+        boolean enabled = false;
+        assert enabled = true;
+        return enabled || graalRuntime().getConfig().cAssertions;
+    }
+
+    public static final Descriptor EXCEPTION_HANDLER_FOR_RETURN_ADDRESS = descriptorFor(UnwindExceptionToCallerStub.class, "exceptionHandlerForReturnAddress", false);
+
+    @NodeIntrinsic(value = CRuntimeCall.class, setStampFromReturnType = true)
+    public static native Word exceptionHandlerForReturnAddress(@ConstantNodeParameter Descriptor exceptionHandlerForReturnAddress, Word thread, Word returnAddress);
+}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ControlSinkNode.java	Mon May 06 15:25:18 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ControlSinkNode.java	Mon May 06 15:25:33 2013 +0200
@@ -22,9 +22,10 @@
  */
 package com.oracle.graal.nodes;
 
+import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.type.*;
 
-public abstract class ControlSinkNode extends FixedNode {
+public abstract class ControlSinkNode extends FixedNode implements Node.IterableNodeType {
 
     public ControlSinkNode(Stamp stamp) {
         super(stamp);
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/UnwindNode.java	Mon May 06 15:25:18 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/UnwindNode.java	Mon May 06 15:25:33 2013 +0200
@@ -28,8 +28,7 @@
 import com.oracle.graal.nodes.type.*;
 
 /**
- * Unwind takes an exception object, destroys the current stack frame and passes the exception
- * object to the system's exception dispatch code.
+ * Unwinds the current frame to an exception handler in the caller frame.
  */
 public final class UnwindNode extends ControlSinkNode implements Lowerable, LIRLowerable, Node.IterableNodeType {
 
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/util/GraphOrder.java	Mon May 06 15:25:18 2013 +0200
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/util/GraphOrder.java	Mon May 06 15:25:33 2013 +0200
@@ -39,13 +39,7 @@
 
         NodeBitMap visited = graph.createNodeBitMap();
 
-        for (ReturnNode node : graph.getNodes(ReturnNode.class)) {
-            result.visitForward(visited, node);
-        }
-        for (UnwindNode node : graph.getNodes(UnwindNode.class)) {
-            result.visitForward(visited, node);
-        }
-        for (DeoptimizeNode node : graph.getNodes(DeoptimizeNode.class)) {
+        for (ControlSinkNode node : graph.getNodes(ControlSinkNode.class)) {
             result.visitForward(visited, node);
         }
         return result;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/UnwindExceptionToCallerTest.java	Mon May 06 15:25:33 2013 +0200
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2011, 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.replacements.test;
+
+import org.junit.*;
+
+import com.oracle.graal.compiler.test.*;
+
+/**
+ * Tests exception throwing from Graal compiled code.
+ */
+public class UnwindExceptionToCallerTest extends GraalCompilerTest {
+
+    @Test
+    public void test1() {
+        NullPointerException npe = new NullPointerException();
+        test("test1Snippet", "a string", npe);
+        test("test1Snippet", (String) null, npe);
+    }
+
+    public static String test1Snippet(String message, NullPointerException npe) {
+        if (message == null) {
+            throw npe;
+        }
+        return message;
+    }
+}
--- a/src/cpu/x86/vm/graalRuntime_x86.cpp	Mon May 06 15:25:18 2013 +0200
+++ b/src/cpu/x86/vm/graalRuntime_x86.cpp	Mon May 06 15:25:33 2013 +0200
@@ -612,95 +612,6 @@
 #endif // _LP64
 }
 
-void GraalRuntime::generate_unwind_exception(GraalStubAssembler *sasm) {
-  // incoming parameters
-  const Register exception_oop = rax;
-  // callee-saved copy of exception_oop during runtime call
-  const Register exception_oop_callee_saved = NOT_LP64(rsi) LP64_ONLY(r14);
-  // other registers used in this stub
-  const Register exception_pc = rdx;
-  const Register handler_addr = rbx;
-  const Register thread = NOT_LP64(rdi) LP64_ONLY(r15_thread);
-
-  // verify that only rax is valid at this time
-#ifdef ASSERT
-  __ movptr(rbx, 0xDEAD);
-  __ movptr(rcx, 0xDEAD);
-  __ movptr(rdx, 0xDEAD);
-  __ movptr(rsi, 0xDEAD);
-  __ movptr(rdi, 0xDEAD);
-#endif
-
-#ifdef ASSERT
-  // check that fields in JavaThread for exception oop and issuing pc are empty
-  NOT_LP64(__ get_thread(thread);)
-  Label oop_empty;
-  __ cmpptr(Address(thread, JavaThread::exception_oop_offset()), 0);
-  __ jcc(Assembler::equal, oop_empty);
-  __ stop("exception oop must be empty");
-  __ bind(oop_empty);
-
-  Label pc_empty;
-  __ cmpptr(Address(thread, JavaThread::exception_pc_offset()), 0);
-  __ jcc(Assembler::equal, pc_empty);
-  __ stop("exception pc must be empty");
-  __ bind(pc_empty);
-#endif
-
-  // clear the FPU stack in case any FPU results are left behind
-  __ empty_FPU_stack();
-
-  // save exception_oop in callee-saved register to preserve it during runtime calls
-  __ verify_not_null_oop(exception_oop);
-  __ movptr(exception_oop_callee_saved, exception_oop);
-
-  NOT_LP64(__ get_thread(thread);)
-  // Get return address (is on top of stack after leave).
-  __ movptr(exception_pc, Address(rsp, 0));
-
-  // search the exception handler address of the caller (using the return address)
-  __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), thread, exception_pc);
-  // rax: exception handler address of the caller
-
-  // Only RAX and RSI are valid at this time, all other registers have been destroyed by the call.
-#ifdef ASSERT
-  __ movptr(rbx, 0xDEAD);
-  __ movptr(rcx, 0xDEAD);
-  __ movptr(rdx, 0xDEAD);
-  __ movptr(rdi, 0xDEAD);
-#endif
-
-  // move result of call into correct register
-  __ movptr(handler_addr, rax);
-
-  // Restore exception oop to RAX (required convention of exception handler).
-  __ movptr(exception_oop, exception_oop_callee_saved);
-
-  // verify that there is really a valid exception in rax
-  __ verify_not_null_oop(exception_oop);
-
-  // get throwing pc (= return address).
-  // rdx has been destroyed by the call, so it must be set again
-  // the pop is also necessary to simulate the effect of a ret(0)
-  __ pop(exception_pc);
-
-  // Restore SP from BP if the exception PC is a method handle call site.
-  NOT_LP64(__ get_thread(thread);)
-  __ cmpl(Address(thread, JavaThread::is_method_handle_return_offset()), 0);
-  __ cmovptr(Assembler::notEqual, rsp, rbp_mh_SP_save);
-
-  // continue at exception handler (return address removed)
-  // note: do *not* remove arguments when unwinding the
-  //       activation since the caller assumes having
-  //       all arguments on the stack when entering the
-  //       runtime to determine the exception handler
-  //       (GC happens at call site with arguments!)
-  // rax: exception oop
-  // rdx: throwing pc
-  // rbx: exception handler
-  __ jmp(handler_addr);
-}
-
 OopMapSet* GraalRuntime::generate_code_for(StubID id, GraalStubAssembler* sasm) {
 
   // for better readability
@@ -714,19 +625,6 @@
   OopMapSet* oop_maps = NULL;
   switch (id) {
 
-    case unwind_exception_call_id: {
-      // remove the frame from the stack
-      __ movptr(rsp, rbp);
-      __ pop(rbp);
-
-      __ set_info("unwind_exception", dont_gc_arguments);
-      // note: no stubframe since we are about to leave the current
-      //       activation and we are calling a leaf VM function only.
-      generate_unwind_exception(sasm);
-      __ should_not_reach_here();
-      break;
-    }
-
     case OSR_migration_end_id: {
     __ enter();
     save_live_registers(sasm, 0);
--- a/src/share/vm/graal/graalCompilerToVM.cpp	Mon May 06 15:25:18 2013 +0200
+++ b/src/share/vm/graal/graalCompilerToVM.cpp	Mon May 06 15:25:33 2013 +0200
@@ -666,6 +666,7 @@
   set_int("threadTlabTopOffset", in_bytes(JavaThread::tlab_top_offset()));
   set_int("threadTlabEndOffset", in_bytes(JavaThread::tlab_end_offset()));
   set_int("threadObjectOffset", in_bytes(JavaThread::threadObj_offset()));
+  set_int("threadIsMethodHandleReturnOffset", in_bytes(JavaThread::is_method_handle_return_offset()));
   set_int("osThreadOffset", in_bytes(JavaThread::osthread_offset()));
   set_int("osThreadInterruptedOffset", in_bytes(OSThread::interrupted_offset()));
   set_int("unlockedMask", (int) markOopDesc::unlocked_value);
@@ -761,7 +762,6 @@
   set_address("monitorExitStub", GraalRuntime::entry_for(GraalRuntime::monitorexit_id));
   set_address("verifyOopStub", GraalRuntime::entry_for(GraalRuntime::verify_oop_id));
   set_address("vmErrorStub", GraalRuntime::entry_for(GraalRuntime::vm_error_id));
-  set_address("unwindExceptionStub", GraalRuntime::entry_for(GraalRuntime::unwind_exception_call_id));
   set_address("osrMigrationEndStub", GraalRuntime::entry_for(GraalRuntime::OSR_migration_end_id));
   set_address("createNullPointerExceptionStub", GraalRuntime::entry_for(GraalRuntime::create_null_pointer_exception_id));
   set_address("createOutOfBoundsExceptionStub", GraalRuntime::entry_for(GraalRuntime::create_out_of_bounds_exception_id));
@@ -788,7 +788,8 @@
   set_address("uncommonTrapStub", SharedRuntime::deopt_blob()->uncommon_trap());
   set_address("vmMessageAddress", GraalRuntime::vm_message);
   set_address("identityHashCodeAddress", GraalRuntime::identity_hash_code);
-  set_address("handleExceptionForPcAddress", GraalRuntime::exception_handler_for_pc);
+  set_address("exceptionHandlerForPcAddress", GraalRuntime::exception_handler_for_pc);
+  set_address("exceptionHandlerForReturnAddressAddress", SharedRuntime::exception_handler_for_return_address);
 
   set_int("deoptReasonNone", Deoptimization::Reason_none);
   set_int("deoptReasonNullCheck", Deoptimization::Reason_null_check);
--- a/src/share/vm/graal/graalRuntime.cpp	Mon May 06 15:25:18 2013 +0200
+++ b/src/share/vm/graal/graalRuntime.cpp	Mon May 06 15:25:33 2013 +0200
@@ -131,7 +131,6 @@
     case handle_exception_nofpu_id:  // Unused on sparc
 #endif
     case verify_oop_id:
-    case unwind_exception_call_id:
     case OSR_migration_end_id:
     case arithmetic_frem_id:
     case arithmetic_drem_id:
--- a/src/share/vm/graal/graalRuntime.hpp	Mon May 06 15:25:18 2013 +0200
+++ b/src/share/vm/graal/graalRuntime.hpp	Mon May 06 15:25:33 2013 +0200
@@ -81,7 +81,6 @@
 // runtime routines needed by code code generated
 // by Graal.
 #define GRAAL_STUBS(stub, last_entry) \
-  stub(unwind_exception_call)   \
   stub(OSR_migration_end)       \
   stub(arithmetic_frem)         \
   stub(arithmetic_drem)         \
--- a/src/share/vm/runtime/sharedRuntime.cpp	Mon May 06 15:25:18 2013 +0200
+++ b/src/share/vm/runtime/sharedRuntime.cpp	Mon May 06 15:25:33 2013 +0200
@@ -484,6 +484,12 @@
   // Reset method handle flag.
   thread->set_is_method_handle_return(false);
 
+#ifdef GRAAL
+  // Graal's ExceptionHandlerStub expects the thread local exception PC to be clear
+  // and other exception handler continuations do not read it
+  thread->set_exception_pc(NULL);
+#endif
+
   // The fastest case first
   CodeBlob* blob = CodeCache::find_blob(return_address);
   nmethod* nm = (blob != NULL) ? blob->as_nmethod_or_null() : NULL;
@@ -493,10 +499,6 @@
     // native nmethods don't have exception handlers
     assert(!nm->is_native_method(), "no exception handler");
     assert(nm->header_begin() != nm->exception_begin(), "no exception handler");
-#ifdef GRAAL
-    // Graal's ExceptionHandlerStub expects the exception PC stored in the thread to be 0
-    thread->set_exception_pc(NULL);
-#endif
     if (nm->is_deopt_pc(return_address)) {
       return SharedRuntime::deopt_blob()->unpack_with_exception();
     } else {