changeset 9562:99ef9bcb3f32

replaced unwind_exception_call assembler stub with a compiled stub (GRAAL-81)
author Doug Simon <doug.simon@oracle.com>
date Sat, 04 May 2013 23:05:46 +0200
parents 85595218dab0
children 325b394bc535
files graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotJumpToExceptionHandlerInCallerOp.java graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRuntime.java graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotUnwindOp.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotBackend.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotLIRGenerator.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotRuntimeCallTarget.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/JumpToExceptionHandlerInCallerNode.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotReplacementsUtil.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/ExceptionHandlerStub.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/UnwindExceptionToCallerStub.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/UnwindNode.java graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/UnwindExceptionToCallerTest.java src/cpu/x86/vm/graalRuntime_x86.cpp src/share/vm/graal/graalCompilerToVM.cpp src/share/vm/graal/graalRuntime.hpp
diffstat 18 files changed, 387 insertions(+), 160 deletions(-) [+]
line wrap: on
line diff
--- /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	Sat May 04 23:05:46 2013 +0200
@@ -0,0 +1,67 @@
+/*
+ * 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);
+
+        // 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	Sat May 04 22:19:52 2013 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Sat May 04 23:05:46 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.*;
@@ -339,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
@@ -362,6 +363,20 @@
     }
 
     @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);
+    }
+
+    @Override
     public void beforeRegisterAllocation() {
         boolean hasDebugInfo = lir.hasDebugInfo();
         AllocatableValue savedRbp = saveRbp.finalize(hasDebugInfo);
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRuntime.java	Sat May 04 22:19:52 2013 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRuntime.java	Sat May 04 23:05:46 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.*;
@@ -60,14 +59,13 @@
         // 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	Sat May 04 22:19:52 2013 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotUnwindOp.java	Sat May 04 23:05:46 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	Sat May 04 22:19:52 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotBackend.java	Sat May 04 23:05:46 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	Sat May 04 22:19:52 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotLIRGenerator.java	Sat May 04 23:05:46 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	Sat May 04 22:19:52 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotRuntimeCallTarget.java	Sat May 04 23:05:46 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	Sat May 04 22:19:52 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Sat May 04 23:05:46 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	Sat May 04 22:19:52 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java	Sat May 04 23:05:46 2013 +0200
@@ -35,14 +35,16 @@
 import static com.oracle.graal.hotspot.nodes.NewInstanceStubCall.*;
 import static com.oracle.graal.hotspot.nodes.NewMultiArrayStubCall.*;
 import static com.oracle.graal.hotspot.nodes.ThreadIsInterruptedStubCall.*;
+import static com.oracle.graal.hotspot.phases.OnStackReplacementPhase.*;
 import static com.oracle.graal.hotspot.replacements.SystemSubstitutions.*;
+import static com.oracle.graal.hotspot.stubs.ExceptionHandlerStub.*;
 import static com.oracle.graal.hotspot.stubs.IdentityHashCodeStub.*;
-import static com.oracle.graal.hotspot.stubs.ExceptionHandlerStub.*;
 import static com.oracle.graal.hotspot.stubs.NewArrayStub.*;
 import static com.oracle.graal.hotspot.stubs.NewInstanceStub.*;
 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.*;
@@ -66,7 +68,6 @@
 import com.oracle.graal.hotspot.bridge.*;
 import com.oracle.graal.hotspot.bridge.CompilerToVM.CodeInstallResult;
 import com.oracle.graal.hotspot.nodes.*;
-import com.oracle.graal.hotspot.phases.*;
 import com.oracle.graal.hotspot.replacements.*;
 import com.oracle.graal.hotspot.stubs.*;
 import com.oracle.graal.java.*;
@@ -215,7 +216,7 @@
 
         // @formatter:off
 
-        addRuntimeCall(OnStackReplacementPhase.OSR_MIGRATION_END, config.osrMigrationEndStub,
+        addRuntimeCall(OSR_MIGRATION_END, config.osrMigrationEndStub,
                         /*           temps */ null,
                         /*             ret */ ret(Kind.Void),
                         /* arg0:      long */ javaCallingConvention(Kind.Long));
@@ -224,10 +225,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));
@@ -373,6 +383,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);
     }
@@ -458,6 +485,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	Sat May 04 23:05:46 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	Sat May 04 22:19:52 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotReplacementsUtil.java	Sat May 04 23:05:46 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	Sat May 04 22:19:52 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/ExceptionHandlerStub.java	Sat May 04 23:05:46 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");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/UnwindExceptionToCallerStub.java	Sat May 04 23:05:46 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/UnwindNode.java	Sat May 04 22:19:52 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/UnwindNode.java	Sat May 04 23:05:46 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 {
 
--- /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	Sat May 04 23:05:46 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	Sat May 04 22:19:52 2013 +0200
+++ b/src/cpu/x86/vm/graalRuntime_x86.cpp	Sat May 04 23:05:46 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	Sat May 04 22:19:52 2013 +0200
+++ b/src/share/vm/graal/graalCompilerToVM.cpp	Sat May 04 23:05:46 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.hpp	Sat May 04 22:19:52 2013 +0200
+++ b/src/share/vm/graal/graalRuntime.hpp	Sat May 04 23:05:46 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)         \