# HG changeset patch # User Doug Simon # Date 1363358307 -3600 # Node ID 9882af5c85046af2b193255b1dd39464b44dba57 # Parent 59bd299750a86dd8bb943f1030abce52bd1ba450 AMD64 HotSpot backend now models RBP as an incoming parameter that must be preserved until the end of the method. For non-leaf methods, the value is preserved in the special stack slot required by the HotSpot runtime for walking/inspecting frames of such methods. diff -r 59bd299750a8 -r 9882af5c8504 graal/com.oracle.graal.compiler.amd64.test/src/com/oracle/graal/compiler/amd64/test/AMD64AllocatorTest.java --- a/graal/com.oracle.graal.compiler.amd64.test/src/com/oracle/graal/compiler/amd64/test/AMD64AllocatorTest.java Thu Mar 14 18:03:00 2013 +0100 +++ b/graal/com.oracle.graal.compiler.amd64.test/src/com/oracle/graal/compiler/amd64/test/AMD64AllocatorTest.java Fri Mar 15 15:38:27 2013 +0100 @@ -30,7 +30,7 @@ @Test public void test1() { - test("test1snippet", 2, 1, 0); + test("test1snippet", 3, 1, 0); } public static long test1snippet(long x) { @@ -39,7 +39,7 @@ @Test public void test2() { - test("test2snippet", 2, 0, 0); + test("test2snippet", 3, 0, 0); } public static long test2snippet(long x) { diff -r 59bd299750a8 -r 9882af5c8504 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java Thu Mar 14 18:03:00 2013 +0100 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java Fri Mar 15 15:38:27 2013 +0100 @@ -65,7 +65,7 @@ private final DebugInfoBuilder debugInfoBuilder; - private Block currentBlock; + protected Block currentBlock; private ValueNode currentInstruction; private ValueNode lastInstructionPrinted; // Debugging only private FrameState lastState; @@ -745,7 +745,7 @@ protected abstract void emitCall(RuntimeCallTarget callTarget, Value result, Value[] arguments, Value[] temps, Value targetAddress, LIRFrameState info); - private static Value toStackKind(Value value) { + protected static Value toStackKind(Value value) { if (value.getKind().getStackKind() != value.getKind()) { // We only have stack-kinds in the LIR, so convert the operand kind for values from the // calling convention. diff -r 59bd299750a8 -r 9882af5c8504 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackend.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackend.java Thu Mar 14 18:03:00 2013 +0100 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackend.java Fri Mar 15 15:38:27 2013 +0100 @@ -25,8 +25,11 @@ 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.phases.GraalOptions.*; import java.lang.reflect.*; +import java.util.*; import sun.misc.*; @@ -46,10 +49,12 @@ import com.oracle.graal.hotspot.nodes.*; import com.oracle.graal.hotspot.stubs.*; import com.oracle.graal.lir.*; +import com.oracle.graal.lir.StandardOp.ParametersOp; import com.oracle.graal.lir.amd64.*; -import com.oracle.graal.lir.amd64.AMD64Move.CompareAndSwapOp; +import com.oracle.graal.lir.amd64.AMD64Move.*; import com.oracle.graal.lir.asm.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.cfg.*; import com.oracle.graal.nodes.java.*; import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; import com.oracle.graal.phases.*; @@ -83,6 +88,69 @@ super(graph, runtime, target, frameMap, method, lir); } + /** + * The slot reserved for storing the original return address when a frame is marked for + * deoptimization. The return address slot in the callee is overwritten with the address of + * a deoptimization stub. + */ + StackSlot deoptimizationRescueSlot; + + /** + * The position at which the instruction for saving RBP should be inserted. + */ + Block saveRbpBlock; + int saveRbpIndex; + + /** + * The slot reserved for saving RBP. + */ + StackSlot rbpSlot; + + /** + * List of epilogue operations that need to restore RBP. + */ + List epilogueOps = new ArrayList<>(2); + + @Override + protected void emitPrologue() { + + CallingConvention incomingArguments = createCallingConvention(); + + RegisterValue rbpParam = rbp.asValue(Kind.Long); + Value[] params = new Value[incomingArguments.getArgumentCount() + 1]; + for (int i = 0; i < params.length - 1; i++) { + params[i] = toStackKind(incomingArguments.getArgument(i)); + if (isStackSlot(params[i])) { + StackSlot slot = ValueUtil.asStackSlot(params[i]); + if (slot.isInCallerFrame() && !lir.hasArgInCallerFrame()) { + lir.setHasArgInCallerFrame(); + } + } + } + params[params.length - 1] = rbpParam; + + ParametersOp paramsOp = new ParametersOp(params); + append(paramsOp); + saveRbpBlock = currentBlock; + saveRbpIndex = lir.lir(saveRbpBlock).size(); + append(paramsOp); // placeholder + rbpSlot = frameMap.allocateSpillSlot(Kind.Long); + assert rbpSlot.getRawOffset() == -16 : rbpSlot.getRawOffset(); + + for (LocalNode local : graph.getNodes(LocalNode.class)) { + Value param = params[local.index()]; + assert param.getKind() == local.kind().getStackKind(); + setResult(local, emitMove(param)); + } + } + + @Override + protected void emitReturn(Value input) { + AMD64HotSpotReturnOp op = new AMD64HotSpotReturnOp(input); + epilogueOps.add(op); + append(op); + } + @Override protected boolean needOnlyOopMaps() { // Stubs only need oop maps @@ -178,28 +246,38 @@ @Override public void emitUnwind(Value exception) { - RegisterValue exceptionParameter = AMD64.rax.asValue(); + RegisterValue exceptionParameter = EXCEPTION.asValue(); emitMove(exceptionParameter, exception); - append(new AMD64HotSpotUnwindOp(exceptionParameter)); + AMD64HotSpotUnwindOp op = new AMD64HotSpotUnwindOp(exceptionParameter); + epilogueOps.add(op); + append(op); } @Override public void emitDeoptimize(DeoptimizationAction action, DeoptimizationReason reason) { - append(new AMD64DeoptimizeOp(action, reason, state(reason))); + append(new AMD64DeoptimizeOp(action, reason, state())); } - /** - * The slot reserved for storing the original return address when a frame is marked for - * deoptimization. The return address slot in the callee is overwritten with the address of - * a deoptimization stub. - */ - StackSlot deoptimizationRescueSlot; - @Override public void beforeRegisterAllocation() { + assert rbpSlot != null; + RegisterValue rbpParam = rbp.asValue(Kind.Long); + AllocatableValue savedRbp; + LIRInstruction saveRbp; if (lir.hasDebugInfo()) { + savedRbp = rbpSlot; deoptimizationRescueSlot = frameMap.allocateSpillSlot(Kind.Long); + } else { + frameMap.freeSpillSlot(rbpSlot); + savedRbp = newVariable(Kind.Long); } + + for (AMD64HotSpotEpilogueOp op : epilogueOps) { + op.savedRbp = savedRbp; + } + + saveRbp = new MoveFromRegOp(savedRbp, rbpParam); + lir.lir(saveRbpBlock).set(saveRbpIndex, saveRbp); } } @@ -238,8 +316,7 @@ AMD64MacroAssembler asm = (AMD64MacroAssembler) tasm.asm; emitStackOverflowCheck(tasm, false); - asm.push(rbp); - asm.decrementq(rsp, frameSize - 8); // account for the push of RBP above + asm.decrementq(rsp, frameSize); if (GraalOptions.ZapStackOnMethodEntry) { final int intSize = 4; for (int i = 0; i < frameSize / intSize; ++i) { @@ -267,8 +344,7 @@ asm.restore(csl, frameToCSA); } - asm.incrementq(rsp, frameSize - 8); // account for the pop of RBP below - asm.pop(rbp); + asm.incrementq(rsp, frameSize); } } @@ -279,16 +355,16 @@ // - has no callee-saved registers // - has no incoming arguments passed on the stack // - has no instructions with debug info - FrameMap frameMap = lirGen.frameMap; - LIR lir = lirGen.lir; - boolean omitFrame = GraalOptions.CanOmitFrame && frameMap.frameSize() == frameMap.initialFrameSize && frameMap.registerConfig.getCalleeSaveLayout().registers.length == 0 && - !lir.hasArgInCallerFrame() && !lir.hasDebugInfo(); + HotSpotAMD64LIRGenerator gen = (HotSpotAMD64LIRGenerator) lirGen; + FrameMap frameMap = gen.frameMap; + LIR lir = gen.lir; + boolean omitFrame = CanOmitFrame && !frameMap.frameNeedsAllocating() && !lir.hasArgInCallerFrame(); AbstractAssembler masm = new AMD64MacroAssembler(target, frameMap.registerConfig); HotSpotFrameContext frameContext = omitFrame ? null : new HotSpotFrameContext(); TargetMethodAssembler tasm = new TargetMethodAssembler(target, runtime(), frameMap, masm, frameContext, compilationResult); tasm.setFrameSize(frameMap.frameSize()); - StackSlot deoptimizationRescueSlot = ((HotSpotAMD64LIRGenerator) lirGen).deoptimizationRescueSlot; + StackSlot deoptimizationRescueSlot = gen.deoptimizationRescueSlot; if (deoptimizationRescueSlot != null) { tasm.compilationResult.setCustomStackAreaOffset(frameMap.offsetForStackSlot(deoptimizationRescueSlot)); } diff -r 59bd299750a8 -r 9882af5c8504 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotEpilogueOp.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotEpilogueOp.java Fri Mar 15 15:38:27 2013 +0100 @@ -0,0 +1,45 @@ +/* + * 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.lir.LIRInstruction.OperandFlag.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.lir.*; +import com.oracle.graal.lir.amd64.*; + +/** + * Superclass for operations that use the value of RBP saved in a method's prologue. + */ +abstract class AMD64HotSpotEpilogueOp extends AMD64LIRInstruction { + + /** + * The type of location (i.e., stack or register) in which RBP is saved is not known until + * initial LIR generation is finished. Until then, we use a placeholder variable so that LIR + * verification is successful. + */ + private static final Variable PLACEHOLDER = new Variable(Kind.Long, Integer.MAX_VALUE, Register.RegisterFlag.CPU); + + @Use({REG, STACK}) protected AllocatableValue savedRbp = PLACEHOLDER; +} diff -r 59bd299750a8 -r 9882af5c8504 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRegisterConfig.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRegisterConfig.java Thu Mar 14 18:03:00 2013 +0100 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRegisterConfig.java Fri Mar 15 15:38:27 2013 +0100 @@ -111,12 +111,7 @@ }; csl = new CalleeSaveLayout(0, -1, 8, regs); } else { - // We reserve space for saving RBP but don't explicitly specify - // it as a callee save register since we explicitly do the saving - // with push and pop in HotSpotFrameContext - final int size = 8; - final Register[] regs = {}; - csl = new CalleeSaveLayout(0, size, 8, regs); + csl = null; } attributesMap = RegisterAttributes.createMap(this, AMD64.allRegisters); diff -r 59bd299750a8 -r 9882af5c8504 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotReturnOp.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotReturnOp.java Fri Mar 15 15:38:27 2013 +0100 @@ -0,0 +1,63 @@ +/* + * 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.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.asm.amd64.*; +import com.oracle.graal.lir.LIRInstruction.Opcode; +import com.oracle.graal.lir.asm.*; + +/** + * Performs an unwind to throw an exception. + */ +@Opcode("RETURN") +final class AMD64HotSpotReturnOp extends AMD64HotSpotEpilogueOp { + + @Use({REG, ILLEGAL}) protected Value value; + + AMD64HotSpotReturnOp(Value value) { + this.value = value; + } + + @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); + } + masm.ret(0); + } +} diff -r 59bd299750a8 -r 9882af5c8504 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 Thu Mar 14 18:03:00 2013 +0100 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRuntime.java Fri Mar 15 15:38:27 2013 +0100 @@ -61,9 +61,9 @@ // @formatter:off addRuntimeCall(AMD64HotSpotUnwindOp.UNWIND_EXCEPTION, config.unwindExceptionStub, - /* temps */ null, - /* ret */ ret(Kind.Void), - /* arg0: exception */ javaCallingConvention(Kind.Object)); + /* temps */ null, + /* ret */ ret(Kind.Void), + /* arg0: exception */ rax.asValue(Kind.Object)); addRuntimeCall(DEOPTIMIZE, config.deoptimizeStub, /* temps */ null, diff -r 59bd299750a8 -r 9882af5c8504 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotUnwindOp.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotUnwindOp.java Thu Mar 14 18:03:00 2013 +0100 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotUnwindOp.java Fri Mar 15 15:38:27 2013 +0100 @@ -23,11 +23,12 @@ 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.amd64.*; import com.oracle.graal.api.code.*; -import com.oracle.graal.api.code.RuntimeCallTarget.*; +import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor; import com.oracle.graal.asm.amd64.*; import com.oracle.graal.lir.LIRInstruction.Opcode; import com.oracle.graal.lir.amd64.*; @@ -36,24 +37,39 @@ /** * Performs an unwind to throw an exception. */ -@Opcode("CALL_INDIRECT") -final class AMD64HotSpotUnwindOp extends AMD64LIRInstruction { +@Opcode("UNWIND") +final class AMD64HotSpotUnwindOp extends AMD64HotSpotEpilogueOp { public static final Descriptor UNWIND_EXCEPTION = new Descriptor("unwindException", true, void.class, Object.class); + /** + * Unwind stub expects the exception in RAX. + */ + public static final Register EXCEPTION = AMD64.rax; + @Use({REG}) protected AllocatableValue exception; - @Temp private RegisterValue framePointer; AMD64HotSpotUnwindOp(AllocatableValue exception) { this.exception = exception; - assert exception == AMD64.rax.asValue(); - framePointer = AMD64.rbp.asValue(); + assert asRegister(exception) == EXCEPTION; } @Override public void emitCode(TargetMethodAssembler tasm, AMD64MacroAssembler masm) { - masm.movq(framePointer.getRegister(), rsp); - masm.incrementq(framePointer.getRegister(), tasm.frameMap.frameSize() - 8); + // 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)); + } + + // Pass the address of the RBP slot in RBP itself + masm.leaq(rbp, rbpSlot); AMD64Call.directCall(tasm, masm, tasm.runtime.lookupRuntimeCall(UNWIND_EXCEPTION), null); } } diff -r 59bd299750a8 -r 9882af5c8504 graal/com.oracle.graal.lir/src/com/oracle/graal/lir/FrameMap.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/FrameMap.java Thu Mar 14 18:03:00 2013 +0100 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/FrameMap.java Fri Mar 15 15:38:27 2013 +0100 @@ -83,14 +83,9 @@ public final RegisterConfig registerConfig; /** - * The initial frame size, not including the size of the return address. This is the constant - * space reserved by the runtime for all compiled methods. - */ - public final int initialFrameSize; - - /** - * The final frame size, not including the size of the return address. The value is only set - * after register allocation is complete, i.e., after all spill slots have been allocated. + * The final frame size, not including the size of the + * {@link Architecture#getReturnAddressSize() return address slot}. The value is only set after + * register allocation is complete, i.e., after all spill slots have been allocated. */ private int frameSize; @@ -127,7 +122,6 @@ this.spillSize = returnAddressSize() + calleeSaveAreaSize(); this.outgoingSize = runtime.getMinimumOutgoingSize(); this.objectStackBlocks = new ArrayList<>(); - this.initialFrameSize = currentFrameSize(); } private int returnAddressSize() { @@ -148,7 +142,8 @@ } /** - * Gets the frame size of the compiled frame, not including the size of the return address. + * Gets the frame size of the compiled frame, not including the size of the + * {@link Architecture#getReturnAddressSize() return address slot}. * * @return The size of the frame (in bytes). */ @@ -158,7 +153,17 @@ } /** - * Gets the total frame size of the compiled frame, including the size of the return address. + * Determines if any space is used in the frame apart from the + * {@link Architecture#getReturnAddressSize() return address slot}. + */ + public boolean frameNeedsAllocating() { + int unalignedFrameSize = outgoingSize + spillSize - returnAddressSize(); + return unalignedFrameSize != 0; + } + + /** + * Gets the total frame size of the compiled frame, including the size of the + * {@link Architecture#getReturnAddressSize() return address slot}. * * @return The total size of the frame (in bytes). */ @@ -242,11 +247,36 @@ */ public StackSlot allocateSpillSlot(Kind kind) { assert frameSize == -1 : "frame size must not yet be fixed"; + if (freedSlots != null) { + for (Iterator iter = freedSlots.iterator(); iter.hasNext();) { + StackSlot s = iter.next(); + if (s.getKind() == kind) { + iter.remove(); + if (freedSlots.isEmpty()) { + freedSlots = null; + } + return s; + } + } + } int size = target.sizeInBytes(kind); spillSize = NumUtil.roundUp(spillSize + size, size); return getSlot(kind, 0); } + private List freedSlots; + + /** + * Frees a spill slot that was obtained via {@link #allocateSpillSlot(Kind)} such that it can be + * reused for the next allocation request for the same kind of slot. + */ + public void freeSpillSlot(StackSlot slot) { + if (freedSlots == null) { + freedSlots = new ArrayList<>(); + } + freedSlots.add(slot); + } + /** * Reserves a block of memory in the frame of the method being compiled. The returned block is * aligned on a word boundary. If the requested size is 0, the method returns {@code null}. diff -r 59bd299750a8 -r 9882af5c8504 graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIR.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIR.java Thu Mar 14 18:03:00 2013 +0100 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIR.java Fri Mar 15 15:38:27 2013 +0100 @@ -116,7 +116,7 @@ } /** - * Determines if any instruction in the LIR has any debug info associated with it. + * Determines if any instruction in the LIR has debug info associated with it. */ public boolean hasDebugInfo() { for (Block b : linearScanOrder()) {