# HG changeset patch # User Doug Simon # Date 1367423172 -7200 # Node ID 5fa54bf57f8c1023dc749669a59f9557cbc4757f # Parent b8cae7920bca58705e29aeb026869dbe699f95ce replaced exception_handler_nofpu assembler stub with a compiled stub (GRAAL-81) generalized stub printf mechanism to also serve as a fatal VM exit message diff -r b8cae7920bca -r 5fa54bf57f8c graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotCRuntimeCallEpilogueOp.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotCRuntimeCallEpilogueOp.java Wed May 01 15:53:17 2013 +0200 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotCRuntimeCallEpilogueOp.java Wed May 01 17:46:12 2013 +0200 @@ -22,11 +22,9 @@ */ package com.oracle.graal.hotspot.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.meta.*; +import com.oracle.graal.api.code.*; import com.oracle.graal.asm.amd64.*; import com.oracle.graal.hotspot.*; import com.oracle.graal.lir.LIRInstruction.Opcode; @@ -36,17 +34,13 @@ @Opcode("CRUNTIME_CALL_EPILOGUE") final class AMD64HotSpotCRuntimeCallEpilogueOp extends AMD64LIRInstruction { - @Use({REG, ILLEGAL}) protected Value thread; - - AMD64HotSpotCRuntimeCallEpilogueOp(Value thread) { - this.thread = thread; - } - @Override public void emitCode(TargetMethodAssembler tasm, AMD64MacroAssembler masm) { // reset last Java frame: HotSpotVMConfig config = graalRuntime().getConfig(); - masm.movslq(new AMD64Address(asRegister(thread), config.threadLastJavaSpOffset), 0); - masm.movslq(new AMD64Address(asRegister(thread), config.threadLastJavaFpOffset), 0); + Register thread = graalRuntime().getRuntime().threadRegister(); + + masm.movslq(new AMD64Address(thread, config.threadLastJavaSpOffset), 0); + masm.movslq(new AMD64Address(thread, config.threadLastJavaFpOffset), 0); } } diff -r b8cae7920bca -r 5fa54bf57f8c graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotCRuntimeCallPrologueOp.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotCRuntimeCallPrologueOp.java Wed May 01 15:53:17 2013 +0200 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotCRuntimeCallPrologueOp.java Wed May 01 17:46:12 2013 +0200 @@ -23,11 +23,9 @@ 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.meta.*; +import com.oracle.graal.api.code.*; import com.oracle.graal.asm.amd64.*; import com.oracle.graal.lir.LIRInstruction.Opcode; import com.oracle.graal.lir.amd64.*; @@ -36,16 +34,11 @@ @Opcode("CRUNTIME_CALL_PROLOGUE") final class AMD64HotSpotCRuntimeCallPrologueOp extends AMD64LIRInstruction { - @Use({REG, ILLEGAL}) protected Value thread; - - AMD64HotSpotCRuntimeCallPrologueOp(Value thread) { - this.thread = thread; - } - @Override public void emitCode(TargetMethodAssembler tasm, AMD64MacroAssembler masm) { // save last Java frame - masm.movq(new AMD64Address(asRegister(thread), graalRuntime().getConfig().threadLastJavaSpOffset), rsp); + Register thread = graalRuntime().getRuntime().threadRegister(); + masm.movq(new AMD64Address(thread, graalRuntime().getConfig().threadLastJavaSpOffset), rsp); } } diff -r b8cae7920bca -r 5fa54bf57f8c graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java Wed May 01 15:53:17 2013 +0200 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java Wed May 01 17:46:12 2013 +0200 @@ -213,23 +213,17 @@ save = new AMD64SaveRegistersOp(savingMoves, restoringMoves, savedRegisterLocations); append(save); - Value thread = args[0]; - AMD64HotSpotCRuntimeCallPrologueOp op = new AMD64HotSpotCRuntimeCallPrologueOp(thread); - append(op); + append(new AMD64HotSpotCRuntimeCallPrologueOp()); } Variable result = super.emitCall(callTarget, cc, info, args); if (needsCalleeSave) { - - Value thread = args[0]; - AMD64HotSpotCRuntimeCallEpilogueOp op = new AMD64HotSpotCRuntimeCallEpilogueOp(thread); - append(op); + assert !calleeSaveInfo.containsKey(currentRuntimeCallInfo); + calleeSaveInfo.put(currentRuntimeCallInfo, save); - AMD64RestoreRegistersOp restore = new AMD64RestoreRegistersOp(savedRegisterLocations.clone(), save); - AMD64SaveRegistersOp oldValue = calleeSaveInfo.put(currentRuntimeCallInfo, save); - assert oldValue == null; - append(restore); + append(new AMD64HotSpotCRuntimeCallEpilogueOp()); + append(new AMD64RestoreRegistersOp(savedRegisterLocations.clone(), save)); } return result; @@ -334,6 +328,13 @@ } @Override + public void emitPatchReturnAddress(ValueNode address) { + load(operand(address)); + AMD64HotSpotPatchReturnAddressOp op = new AMD64HotSpotPatchReturnAddressOp(load(operand(address))); + append(op); + } + + @Override public void beforeRegisterAllocation() { boolean hasDebugInfo = lir.hasDebugInfo(); AllocatableValue savedRbp = saveRbp.finalize(hasDebugInfo); diff -r b8cae7920bca -r 5fa54bf57f8c graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotPatchReturnAddressOp.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotPatchReturnAddressOp.java Wed May 01 17:46:12 2013 +0200 @@ -0,0 +1,52 @@ +/* + * 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.lir.LIRInstruction.OperandFlag.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.asm.amd64.*; +import com.oracle.graal.lir.LIRInstruction.Opcode; +import com.oracle.graal.lir.amd64.*; +import com.oracle.graal.lir.asm.*; + +/** + * Patch the return address of the current frame. + */ +@Opcode("PATCH_RETURN") +final class AMD64HotSpotPatchReturnAddressOp extends AMD64LIRInstruction { + + @Use(REG) AllocatableValue address; + + AMD64HotSpotPatchReturnAddressOp(AllocatableValue address) { + this.address = address; + } + + @Override + public void emitCode(TargetMethodAssembler tasm, AMD64MacroAssembler masm) { + int frameSize = tasm.frameMap.frameSize(); + masm.movq(new AMD64Address(rsp, frameSize), asRegister(address)); + } +} diff -r b8cae7920bca -r 5fa54bf57f8c graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRuntime.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRuntime.java Wed May 01 15:53:17 2013 +0200 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRuntime.java Wed May 01 17:46:12 2013 +0200 @@ -24,6 +24,7 @@ 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.*; @@ -54,6 +55,11 @@ Kind word = graalRuntime.getTarget().wordKind; // @formatter:off + addStubCall(EXCEPTION_HANDLER, + /* 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), diff -r b8cae7920bca -r 5fa54bf57f8c graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotBackend.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotBackend.java Wed May 01 15:53:17 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotBackend.java Wed May 01 17:46:12 2013 +0200 @@ -22,10 +22,13 @@ */ package com.oracle.graal.hotspot; +import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor; import com.oracle.graal.api.code.*; -import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor; import com.oracle.graal.compiler.target.*; +import com.oracle.graal.hotspot.bridge.*; import com.oracle.graal.hotspot.meta.*; +import com.oracle.graal.hotspot.stubs.*; +import com.oracle.graal.word.*; /** * HotSpot specific backend. @@ -38,9 +41,10 @@ public static final Descriptor UNCOMMON_TRAP = new Descriptor("deoptimize", true, void.class); /** - * Descriptor for GraalRuntime::handle_exception_nofpu_id. + * Descriptor for {@link ExceptionHandlerStub}. This stub is called by the + * {@linkplain Marks#MARK_EXCEPTION_HANDLER_ENTRY exception handler} in a compiled method. */ - public static final Descriptor EXCEPTION_HANDLER = new Descriptor("exceptionHandler", true, void.class); + public static final Descriptor EXCEPTION_HANDLER = new Descriptor("exceptionHandler", true, void.class, Object.class, Word.class); /** * Descriptor for SharedRuntime::deopt_blob()->unpack(). diff -r b8cae7920bca -r 5fa54bf57f8c graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotLIRGenerator.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotLIRGenerator.java Wed May 01 15:53:17 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotLIRGenerator.java Wed May 01 17:46:12 2013 +0200 @@ -26,6 +26,7 @@ import com.oracle.graal.api.meta.*; import com.oracle.graal.compiler.gen.*; import com.oracle.graal.hotspot.nodes.*; +import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.spi.*; /** @@ -44,6 +45,8 @@ void emitDeoptimizeCaller(DeoptimizationAction action, DeoptimizationReason reason); + void emitPatchReturnAddress(ValueNode address); + void visitDirectCompareAndSwap(DirectCompareAndSwapNode x); /** diff -r b8cae7920bca -r 5fa54bf57f8c graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java Wed May 01 15:53:17 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java Wed May 01 17:46:12 2013 +0200 @@ -344,7 +344,6 @@ // runtime stubs public long inlineCacheMissStub; - public long handleExceptionStub; public long handleDeoptStub; public long monitorEnterStub; public long monitorExitStub; @@ -379,8 +378,9 @@ public long newMultiArrayAddress; public long registerFinalizerAddress; public long threadIsInterruptedAddress; - public long stubPrintfAddress; + public long vmMessageAddress; public long identityHashCodeAddress; + public long handleExceptionForPcAddress; public int deoptReasonNullCheck; public int deoptReasonRangeCheck; diff -r b8cae7920bca -r 5fa54bf57f8c graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java Wed May 01 15:53:17 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java Wed May 01 17:46:12 2013 +0200 @@ -37,6 +37,7 @@ import static com.oracle.graal.hotspot.nodes.ThreadIsInterruptedStubCall.*; import static com.oracle.graal.hotspot.replacements.SystemSubstitutions.*; 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.*; @@ -222,6 +223,10 @@ /* temps */ null, /* ret */ ret(Kind.Void)); + addCRuntimeCall(EXCEPTION_HANDLER_FOR_PC, config.handleExceptionForPcAddress, + /* ret */ ret(word), + /* arg0: thread */ nativeCallingConvention(word)); + addStubCall(REGISTER_FINALIZER, /* ret */ ret(Kind.Void), /* arg0: object */ javaCallingConvention(Kind.Object)); @@ -311,12 +316,13 @@ /* arg2: value */ Kind.Long, /* arg3: value */ Kind.Long)); - addCRuntimeCall(STUB_PRINTF_C, config.stubPrintfAddress, + addCRuntimeCall(STUB_PRINTF_C, config.vmMessageAddress, /* ret */ ret(Kind.Void), - /* arg0: format */ nativeCallingConvention(Kind.Long, - /* arg1: value */ Kind.Long, + /* arg0: vmError */ nativeCallingConvention(Kind.Boolean, + /* arg1: format */ word, /* arg2: value */ Kind.Long, - /* arg3: value */ Kind.Long)); + /* arg3: value */ Kind.Long, + /* arg4: value */ Kind.Long)); addRuntimeCall(LOG_OBJECT, config.logObjectStub, /* temps */ null, @@ -335,10 +341,6 @@ /* arg1: receiverThread */ Kind.Object, /* arg1: clearInterrupted */ Kind.Boolean)); - addRuntimeCall(EXCEPTION_HANDLER, config.handleExceptionStub, - /* temps */ null, - /* ret */ ret(Kind.Void)); - addRuntimeCall(DEOPT_HANDLER, config.handleDeoptStub, /* temps */ null, /* ret */ ret(Kind.Void)); @@ -454,6 +456,7 @@ registerStub(new RegisterFinalizerStub(this, replacements, graalRuntime.getTarget(), runtimeCalls.get(REGISTER_FINALIZER))); 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))); } private void registerStub(Stub stub) { diff -r b8cae7920bca -r 5fa54bf57f8c graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/PatchReturnAddressNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/PatchReturnAddressNode.java Wed May 01 17:46:12 2013 +0200 @@ -0,0 +1,50 @@ +/* + * 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.*; + +/** + * Modifies the return address of the current frame. + */ +public class PatchReturnAddressNode extends FixedWithNextNode implements LIRLowerable { + + @Input private ValueNode address; + + public PatchReturnAddressNode(ValueNode address) { + super(StampFactory.forVoid()); + this.address = address; + } + + @Override + public void generate(LIRGeneratorTool gen) { + ((HotSpotLIRGenerator) gen).emitPatchReturnAddress(address); + } + + @NodeIntrinsic + public static native void patchReturnAddress(Word address); +} diff -r b8cae7920bca -r 5fa54bf57f8c graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotSnippetUtils.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotSnippetUtils.java Wed May 01 15:53:17 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotSnippetUtils.java Wed May 01 17:46:12 2013 +0200 @@ -115,6 +115,10 @@ return thread.readObject(threadExceptionOopOffset(), EXCEPTION_OOP_LOCATION); } + public static Word readExceptionPc(Word thread) { + return thread.readWord(threadExceptionOopOffset(), EXCEPTION_PC_LOCATION); + } + public static void writeExceptionOop(Word thread, Object value) { thread.writeObject(threadExceptionOopOffset(), value, EXCEPTION_OOP_LOCATION); } @@ -146,7 +150,7 @@ } /** - * Clears the pending exception if for the given thread. + * Clears the pending exception for the given thread. * * @return {@code true} if there was a pending exception */ diff -r b8cae7920bca -r 5fa54bf57f8c graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/ExceptionHandlerStub.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/ExceptionHandlerStub.java Wed May 01 17:46:12 2013 +0200 @@ -0,0 +1,107 @@ +/* + * 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.nodes.PatchReturnAddressNode.*; +import static com.oracle.graal.hotspot.replacements.HotSpotSnippetUtils.*; + +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.bridge.*; +import com.oracle.graal.hotspot.meta.*; +import com.oracle.graal.hotspot.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 the {@linkplain Marks#MARK_EXCEPTION_HANDLER_ENTRY exception handler entry point} + * in a compiled method. This entry point is used when returning to a method to handle an exception + * thrown by a callee. It is not used for routing implicit exceptions. Therefore, it does not need + * to save any registers as HotSpot uses a caller save convention. + *

+ * The descriptor for a call to this stub is {@link HotSpotBackend#EXCEPTION_HANDLER}. + */ +public class ExceptionHandlerStub extends CRuntimeStub { + + public ExceptionHandlerStub(final HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotRuntimeCallTarget linkage) { + super(runtime, replacements, target, linkage); + } + + @Snippet + private static void exceptionHandler(Object exception, Word exceptionPc) { + checkNoExceptionInThread(); + writeExceptionOop(thread(), exception); + writeExceptionPc(thread(), exceptionPc); + if (logging()) { + printf("handling exception %p at %p\n", Word.fromObject(exception).rawValue(), exceptionPc.rawValue()); + } + + // patch throwing pc into return address so that deoptimization finds the right debug info + patchReturnAddress(exceptionPc); + + // TODO (ds) add support for non-register-preserving C runtime calls and + // use it for this call as no registers need saving by this stub + Word handlerPc = exceptionHandlerForPc(EXCEPTION_HANDLER_FOR_PC, thread()); + + if (logging()) { + printf("handler for exception %p at %p is at %p\n", Word.fromObject(exception).rawValue(), exceptionPc.rawValue(), handlerPc.rawValue()); + } + + // patch the return address so that this stub returns to the exception handler + patchReturnAddress(handlerPc); + } + + private static void checkNoExceptionInThread() { + if (assertionsEnabled()) { + if (readExceptionOop(thread()) != null) { + fatal("exception oop must be null, not %p", Word.fromObject(readExceptionOop(thread())).rawValue()); + } + if (readExceptionPc(thread()).notEqual(Word.zero())) { + fatal("exception pc must be zero, not %p", readExceptionPc(thread()).rawValue()); + } + } + } + + @Fold + private static boolean logging() { + return Boolean.getBoolean("graal.logExceptionHandlerStub"); + } + + @Fold + @SuppressWarnings("all") + private static boolean assertionsEnabled() { + boolean enabled = false; + assert enabled = true; + return enabled; + } + + public static final Descriptor EXCEPTION_HANDLER_FOR_PC = descriptorFor(ExceptionHandlerStub.class, "exceptionHandlerForPc", false); + + @NodeIntrinsic(value = CRuntimeCall.class, setStampFromReturnType = true) + public static native Word exceptionHandlerForPc(@ConstantNodeParameter Descriptor exceptionHandlerForPc, Word thread); +} diff -r b8cae7920bca -r 5fa54bf57f8c graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/Stub.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/Stub.java Wed May 01 15:53:17 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/Stub.java Wed May 01 17:46:12 2013 +0200 @@ -140,7 +140,7 @@ private boolean checkStubInvariants(CompilationResult compResult) { for (DataPatch data : compResult.getDataReferences()) { Constant constant = data.constant; - assert constant.getKind() != Kind.Object : format("%h.%n(%p): ", getMethod()) + "cannot have embedded oop: " + constant; + assert constant.getKind() != Kind.Object : format("%h.%n(%p): ", getMethod()) + "cannot have embedded object constant: " + constant; assert constant.getPrimitiveAnnotation() == null : format("%h.%n(%p): ", getMethod()) + "cannot have embedded metadata: " + constant; } for (Infopoint infopoint : compResult.getInfopoints()) { @@ -220,6 +220,12 @@ 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); @@ -256,26 +262,113 @@ public static final Descriptor STUB_PRINTF_C = descriptorFor(Stub.class, "printfC", false); @NodeIntrinsic(CRuntimeCall.class) - private static native void printfC(@ConstantNodeParameter Descriptor stubPrintf, Word format, long v1, long v2, long v3); + private static native void printfC(@ConstantNodeParameter Descriptor stubPrintfC, boolean vmError, Word format, long v1, long v2, long v3); /** - * Prints a formatted string to the log stream. This differs from {@link Log#LOG_PRINTF} in that - * the format string is a C string in the C heap which avoids having an embedded oop in a - * RuntimeStub. + * Prints a message to the log stream. + *

+ * Stubs must use this instead of {@link Log#printf(String, long)} to avoid an object + * constant in a RuntimeStub. * - * @param format a C style printf format value that can contain at most one conversion specifier - * (i.e., a sequence of characters starting with '%'). - * @param value the value associated with the conversion specifier + * @param message a message string + */ + public static void printf(String message) { + printfC(STUB_PRINTF_C, false, cstring(message), 0L, 0L, 0L); + } + + /** + * Prints a message to the log stream. + *

+ * Stubs must use this instead of {@link Log#printf(String, long)} to avoid an object + * constant in a RuntimeStub. + * + * @param format a C style printf format value + * @param value the value associated with the first conversion specifier in {@code format} */ public static void printf(String format, long value) { - printfC(STUB_PRINTF_C, cstring(format), value, 0L, 0L); + printfC(STUB_PRINTF_C, false, cstring(format), value, 0L, 0L); + } + + /** + * Prints a message to the log stream. + *

+ * Stubs must use this instead of {@link Log#printf(String, long, long)} to avoid an object + * constant in a RuntimeStub. + * + * @param format a C style printf format value + * @param v1 the value associated with the first conversion specifier in {@code format} + * @param v2 the value associated with the second conversion specifier in {@code format} + */ + public static void printf(String format, long v1, long v2) { + printfC(STUB_PRINTF_C, false, cstring(format), v1, v2, 0L); + } + + /** + * Prints a message to the log stream. + *

+ * Stubs must use this instead of {@link Log#printf(String, long, long, long)} to avoid an + * object constant in a RuntimeStub. + * + * @param format a C style printf format value + * @param v1 the value associated with the first conversion specifier in {@code format} + * @param v2 the value associated with the second conversion specifier in {@code format} + * @param v3 the value associated with the third conversion specifier in {@code format} + */ + public static void printf(String format, long v1, long v2, long v3) { + printfC(STUB_PRINTF_C, false, cstring(format), v1, v2, v3); } - public static void printf(String format, long v1, long v2) { - printfC(STUB_PRINTF_C, cstring(format), v1, v2, 0L); + /** + * Exits the VM with a given error message. + *

+ * Stubs must use this instead of {@link VMErrorNode#vmError(String, long)} to avoid an + * object constant in a RuntimeStub. + * + * @param message an error message + */ + public static void fatal(String message) { + printfC(STUB_PRINTF_C, true, cstring(message), 0L, 0L, 0L); + } + + /** + * Exits the VM with a given error message. + *

+ * Stubs must use this instead of {@link Log#printf(String, long, long, long)} to avoid an + * object constant in a RuntimeStub. + * + * @param format a C style printf format value + * @param value the value associated with the first conversion specifier in {@code format} + */ + public static void fatal(String format, long value) { + printfC(STUB_PRINTF_C, true, cstring(format), value, 0L, 0L); } - public static void printf(String format, long v1, long v2, long v3) { - printfC(STUB_PRINTF_C, cstring(format), v1, v2, v3); + /** + * Exits the VM with a given error message. + *

+ * Stubs must use this instead of {@link Log#printf(String, long, long, long)} to avoid an + * object constant in a RuntimeStub. + * + * @param format a C style printf format value + * @param v1 the value associated with the first conversion specifier in {@code format} + * @param v2 the value associated with the second conversion specifier in {@code format} + */ + public static void fatal(String format, long v1, long v2) { + printfC(STUB_PRINTF_C, true, cstring(format), v1, v2, 0L); + } + + /** + * Exits the VM with a given error message. + *

+ * Stubs must use this instead of {@link Log#printf(String, long, long, long)} to avoid an + * object constant in a RuntimeStub. + * + * @param format a C style printf format value + * @param v1 the value associated with the first conversion specifier in {@code format} + * @param v2 the value associated with the second conversion specifier in {@code format} + * @param v3 the value associated with the third conversion specifier in {@code format} + */ + public static void fatal(String format, long v1, long v2, long v3) { + printfC(STUB_PRINTF_C, true, cstring(format), v1, v2, v3); } } diff -r b8cae7920bca -r 5fa54bf57f8c graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/CompiledExceptionHandlerTest.java --- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/CompiledExceptionHandlerTest.java Wed May 01 15:53:17 2013 +0200 +++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/CompiledExceptionHandlerTest.java Wed May 01 17:46:12 2013 +0200 @@ -64,6 +64,7 @@ } test("test1Snippet", "a string"); + test("test1Snippet", (String) null); } public static String test1Snippet(String message) { @@ -76,4 +77,51 @@ } return null; } + + private static void raiseException(String m1, String m2, String m3, String m4, String m5) { + throw new RuntimeException(m1 + m2 + m3 + m4 + m5); + } + + @Test + public void test2() { + // Ensure the profile shows a hot exception + for (int i = 0; i < 10000; i++) { + test2Snippet("m1", "m2", "m3", "m4", "m5"); + test2Snippet(null, "m2", "m3", "m4", "m5"); + } + + test("test2Snippet", "m1", "m2", "m3", "m4", "m5"); + test("test2Snippet", null, "m2", "m3", "m4", "m5"); + } + + public static String test2Snippet(String m1, String m2, String m3, String m4, String m5) { + if (m1 != null) { + try { + raiseException(m1, m2, m3, m4, m5); + } catch (Exception e) { + return m5 + m4 + m3 + m2 + m1; + } + } + return m4 + m3; + } + + @Test + public void test3() { + // Ensure the profile shows a hot exception + for (int i = 0; i < 10000; i++) { + test3Snippet("object1", "object2"); + test3Snippet(null, "object2"); + } + + test("test3Snippet", (Object) null, "object2"); + test("test3Snippet", "object1", "object2"); + } + + public static String test3Snippet(Object o, Object o2) { + try { + return o.toString(); + } catch (NullPointerException e) { + return String.valueOf(o2); + } + } } diff -r b8cae7920bca -r 5fa54bf57f8c graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/DeoptimizeOnExceptionTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/DeoptimizeOnExceptionTest.java Wed May 01 17:46:12 2013 +0200 @@ -0,0 +1,59 @@ +/* + * 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.api.meta.*; +import com.oracle.graal.compiler.test.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.phases.*; +import com.oracle.graal.phases.common.*; + +/** + * Tests that deoptimization upon exception handling works. + */ +public class DeoptimizeOnExceptionTest extends GraalCompilerTest { + + @Override + protected void editPhasePlan(ResolvedJavaMethod method, StructuredGraph graph, PhasePlan phasePlan) { + phasePlan.disablePhase(InliningPhase.class); + } + + private static void raiseException(String m1, String m2, String m3, String m4, String m5) { + throw new RuntimeException(m1 + m2 + m3 + m4 + m5); + } + + @Test + public void test1() { + test("test1Snippet", "m1", "m2", "m3", "m4", "m5"); + } + + // no local exception handler - will deopt + public static String test1Snippet(String m1, String m2, String m3, String m4, String m5) { + if (m1 != null) { + raiseException(m1, m2, m3, m4, m5); + } + return m1 + m2 + m3 + m4 + m5; + } +} diff -r b8cae7920bca -r 5fa54bf57f8c src/cpu/x86/vm/graalRuntime_x86.cpp --- a/src/cpu/x86/vm/graalRuntime_x86.cpp Wed May 01 15:53:17 2013 +0200 +++ b/src/cpu/x86/vm/graalRuntime_x86.cpp Wed May 01 17:46:12 2013 +0200 @@ -612,103 +612,6 @@ #endif // _LP64 } -OopMapSet* GraalRuntime::generate_handle_exception(StubID id, GraalStubAssembler *sasm) { - __ block_comment("generate_handle_exception"); - - // incoming parameters - const Register exception_oop = rax; - const Register exception_pc = rdx; - // other registers used in this stub - const Register thread = NOT_LP64(rdi) LP64_ONLY(r15_thread); - - // Save registers, if required. - OopMapSet* oop_maps = new OopMapSet(); - OopMap* oop_map = NULL; - switch (id) { - case handle_exception_nofpu_id: - // At this point all registers MAY be live. - oop_map = save_live_registers(sasm, 1 /*thread*/, id == handle_exception_nofpu_id); - break; - default: ShouldNotReachHere(); - } - -#ifdef TIERED - // C2 can leave the fpu stack dirty - if (UseSSE < 2) { - __ empty_FPU_stack(); - } -#endif // TIERED - - // verify that only rax, and rdx is valid at this time -#ifdef ASSERT - __ movptr(rbx, 0xDEAD); - __ movptr(rcx, 0xDEAD); - __ movptr(rsi, 0xDEAD); - __ movptr(rdi, 0xDEAD); -#endif - - // verify that rax, contains a valid exception - __ verify_not_null_oop(exception_oop); - - // load address of JavaThread object for thread-local data - NOT_LP64(__ get_thread(thread);) - -#ifdef ASSERT - // check that fields in JavaThread for exception oop and issuing pc are - // empty before writing to them - Label oop_empty; - __ cmpptr(Address(thread, JavaThread::exception_oop_offset()), (int32_t) NULL_WORD); - __ jcc(Assembler::equal, oop_empty); - __ stop("exception oop already set"); - __ bind(oop_empty); - - Label pc_empty; - __ cmpptr(Address(thread, JavaThread::exception_pc_offset()), 0); - __ jcc(Assembler::equal, pc_empty); - __ stop("exception pc already set"); - __ bind(pc_empty); -#endif - - // save exception oop and issuing pc into JavaThread - // (exception handler will load it from here) - __ movptr(Address(thread, JavaThread::exception_oop_offset()), exception_oop); - __ movptr(Address(thread, JavaThread::exception_pc_offset()), exception_pc); - - // patch throwing pc into return address (has bci & oop map) - __ movptr(Address(rbp, 1*BytesPerWord), exception_pc); - - // compute the exception handler. - // the exception oop and the throwing pc are read from the fields in JavaThread - int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, exception_handler_for_pc)); - oop_maps->add_gc_map(call_offset, oop_map); - - // rax: handler address - // will be the deopt blob if nmethod was deoptimized while we looked up - // handler regardless of whether handler existed in the nmethod. - - // only rax, is valid at this time, all other registers have been destroyed by the runtime call -#ifdef ASSERT - __ movptr(rbx, 0xDEAD); - __ movptr(rcx, 0xDEAD); - __ movptr(rdx, 0xDEAD); - __ movptr(rsi, 0xDEAD); - __ movptr(rdi, 0xDEAD); -#endif - - // patch the return address, this stub will directly return to the exception handler - __ movptr(Address(rbp, 1*BytesPerWord), rax); - - switch (id) { - case handle_exception_nofpu_id: - // Restore the registers that were saved at the beginning. - restore_live_registers(sasm, id == handle_exception_nofpu_id); - break; - default: ShouldNotReachHere(); - } - - return oop_maps; -} - void GraalRuntime::generate_unwind_exception(GraalStubAssembler *sasm) { // incoming parameters const Register exception_oop = rax; @@ -811,12 +714,6 @@ OopMapSet* oop_maps = NULL; switch (id) { - case handle_exception_nofpu_id: - { GraalStubFrame f(sasm, "handle_exception", dont_gc_arguments); - oop_maps = generate_handle_exception(id, sasm); - } - break; - case unwind_exception_call_id: { // remove the frame from the stack __ movptr(rsp, rbp); diff -r b8cae7920bca -r 5fa54bf57f8c src/share/vm/graal/graalCompilerToVM.cpp --- a/src/share/vm/graal/graalCompilerToVM.cpp Wed May 01 15:53:17 2013 +0200 +++ b/src/share/vm/graal/graalCompilerToVM.cpp Wed May 01 17:46:12 2013 +0200 @@ -755,7 +755,6 @@ set_address("wbPostCallStub", GraalRuntime::entry_for(GraalRuntime::wb_post_call_id)); set_address("inlineCacheMissStub", SharedRuntime::get_ic_miss_stub()); - set_address("handleExceptionStub", GraalRuntime::entry_for(GraalRuntime::handle_exception_nofpu_id)); set_address("handleDeoptStub", SharedRuntime::deopt_blob()->unpack()); set_address("monitorEnterStub", GraalRuntime::entry_for(GraalRuntime::monitorenter_id)); set_address("monitorExitStub", GraalRuntime::entry_for(GraalRuntime::monitorexit_id)); @@ -786,8 +785,9 @@ set_address("registerFinalizerAddress", SharedRuntime::register_finalizer); set_address("threadIsInterruptedAddress", GraalRuntime::thread_is_interrupted); set_address("uncommonTrapStub", SharedRuntime::deopt_blob()->uncommon_trap()); - set_address("stubPrintfAddress", GraalRuntime::stub_printf); + set_address("vmMessageAddress", GraalRuntime::vm_message); set_address("identityHashCodeAddress", GraalRuntime::identity_hash_code); + set_address("handleExceptionForPcAddress", GraalRuntime::exception_handler_for_pc); set_int("deoptReasonNone", Deoptimization::Reason_none); set_int("deoptReasonNullCheck", Deoptimization::Reason_null_check); diff -r b8cae7920bca -r 5fa54bf57f8c src/share/vm/graal/graalRuntime.cpp --- a/src/share/vm/graal/graalRuntime.cpp Wed May 01 15:53:17 2013 +0200 +++ b/src/share/vm/graal/graalRuntime.cpp Wed May 01 17:46:12 2013 +0200 @@ -402,7 +402,6 @@ } } - thread->set_vm_result(exception()); // Set flag if return address is a method handle call site. thread->set_is_method_handle_return(nm->is_method_handle_return(pc)); @@ -570,10 +569,14 @@ tty->print(buf, v1, v2, v3); JRT_END -JRT_LEAF(void, GraalRuntime::stub_printf(jlong format, jlong v1, jlong v2, jlong v3)) +JRT_LEAF(void, GraalRuntime::vm_message(jboolean vmError, jlong format, jlong v1, jlong v2, jlong v3)) ResourceMark rm; char *buf = (char*) (address) format; - tty->print(buf, v1, v2, v3); + if (vmError) { + fatal(err_msg(buf, v1, v2, v3)); + } else { + tty->print(buf, v1, v2, v3); + } JRT_END JRT_ENTRY(void, GraalRuntime::log_primitive(JavaThread* thread, jchar typeChar, jlong value, jboolean newline)) diff -r b8cae7920bca -r 5fa54bf57f8c src/share/vm/graal/graalRuntime.hpp --- a/src/share/vm/graal/graalRuntime.hpp Wed May 01 15:53:17 2013 +0200 +++ b/src/share/vm/graal/graalRuntime.hpp Wed May 01 17:46:12 2013 +0200 @@ -81,7 +81,6 @@ // runtime routines needed by code code generated // by Graal. #define GRAAL_STUBS(stub, last_entry) \ - stub(handle_exception_nofpu) /* optimized version that does not preserve fpu registers */ \ stub(unwind_exception_call) \ stub(OSR_migration_end) \ stub(arithmetic_frem) \ @@ -119,7 +118,6 @@ // stub generation static void generate_blob_for(BufferBlob* blob, StubID id); static OopMapSet* generate_code_for(StubID id, GraalStubAssembler* sasm); - static OopMapSet* generate_handle_exception(StubID id, GraalStubAssembler* sasm); static void generate_unwind_exception(GraalStubAssembler *sasm); static OopMapSet* generate_stub_call(GraalStubAssembler* sasm, Register result, address entry, @@ -128,8 +126,6 @@ // runtime entry points static void unimplemented_entry(JavaThread* thread, StubID id); - static address exception_handler_for_pc(JavaThread* thread); - static void create_null_exception(JavaThread* thread); static void create_out_of_bounds_exception(JavaThread* thread, jint index); static void monitorenter(JavaThread* thread, oopDesc* obj, BasicLock* lock); @@ -154,8 +150,10 @@ static void new_array(JavaThread* thread, Klass* klass, jint length); static void new_multi_array(JavaThread* thread, Klass* klass, int rank, jint* dims); static jboolean thread_is_interrupted(JavaThread* thread, oopDesc* obj, jboolean clear_interrupte); - static void stub_printf(jlong format, jlong v1, jlong v2, jlong v3); + static void vm_message(jboolean vmError, jlong format, jlong v1, jlong v2, jlong v3); static jint identity_hash_code(JavaThread* thread, oopDesc* objd); + static address exception_handler_for_pc(JavaThread* thread); + // initialization static void initialize(BufferBlob* blob);