001/* 002 * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.hotspot.stubs; 024 025import static com.oracle.graal.hotspot.HotSpotBackend.*; 026import static com.oracle.graal.hotspot.HotSpotBackend.Options.*; 027import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*; 028import jdk.internal.jvmci.code.*; 029import jdk.internal.jvmci.common.*; 030import jdk.internal.jvmci.meta.*; 031 032import com.oracle.graal.api.replacements.*; 033import com.oracle.graal.asm.*; 034import com.oracle.graal.compiler.common.spi.*; 035import com.oracle.graal.graph.Node.ConstantNodeParameter; 036import com.oracle.graal.graph.Node.NodeIntrinsic; 037import com.oracle.graal.hotspot.*; 038import com.oracle.graal.hotspot.meta.*; 039import com.oracle.graal.hotspot.nodes.*; 040import com.oracle.graal.nodes.*; 041import com.oracle.graal.replacements.*; 042import com.oracle.graal.replacements.Snippet.ConstantParameter; 043import com.oracle.graal.word.*; 044 045/** 046 * Uncommon trap stub. 047 * 048 * This is the entry point for code which is returning to a de-optimized frame. 049 * 050 * The steps taken by this frame are as follows: 051 * 052 * <li>push a dummy "register_save" and save the return values (O0, O1, F0/F1, G1) and all 053 * potentially live registers (at a pollpoint many registers can be live). 054 * 055 * <li>call the C routine: Deoptimization::fetch_unroll_info (this function returns information 056 * about the number and size of interpreter frames which are equivalent to the frame which is being 057 * deoptimized) 058 * 059 * <li>deallocate the unpack frame, restoring only results values. Other volatile registers will now 060 * be captured in the vframeArray as needed. 061 * 062 * <li>deallocate the deoptimization frame 063 * 064 * <li>in a loop using the information returned in the previous step push new interpreter frames 065 * (take care to propagate the return values through each new frame pushed) 066 * 067 * <li>create a dummy "unpack_frame" and save the return values (O0, O1, F0) 068 * 069 * <li>call the C routine: Deoptimization::unpack_frames (this function lays out values on the 070 * interpreter frame which was just created) 071 * 072 * <li>deallocate the dummy unpack_frame 073 * 074 * <li>ensure that all the return values are correctly set and then do a return to the interpreter 075 * entry point 076 * 077 * <p> 078 * <b>ATTENTION: We cannot do any complicated operations e.g. logging via printf in this snippet 079 * because we change the current stack layout and so the code is very sensitive to register 080 * allocation.</b> 081 */ 082public class UncommonTrapStub extends SnippetStub { 083 084 public static final LocationIdentity STACK_BANG_LOCATION = NamedLocationIdentity.mutable("stack bang"); 085 086 private final TargetDescription target; 087 088 public UncommonTrapStub(HotSpotProviders providers, TargetDescription target, HotSpotForeignCallLinkage linkage) { 089 super(UncommonTrapStub.class, "uncommonTrapHandler", providers, linkage); 090 this.target = target; 091 assert PreferGraalStubs.getValue(); 092 } 093 094 @Override 095 public boolean preservesRegisters() { 096 return false; 097 } 098 099 @Override 100 protected Object getConstantParameterValue(int index, String name) { 101 switch (index) { 102 case 0: 103 return providers.getRegisters().getThreadRegister(); 104 case 1: 105 return providers.getRegisters().getStackPointerRegister(); 106 default: 107 throw JVMCIError.shouldNotReachHere("unknown parameter " + name + " at index " + index); 108 } 109 } 110 111 /** 112 * Uncommon trap handler. 113 * 114 * We save the argument return registers. We call the first C routine, fetch_unroll_info(). This 115 * routine captures the return values and returns a structure which describes the current frame 116 * size and the sizes of all replacement frames. The current frame is compiled code and may 117 * contain many inlined functions, each with their own JVM state. We pop the current frame, then 118 * push all the new frames. Then we call the C routine unpack_frames() to populate these frames. 119 * Finally unpack_frames() returns us the new target address. Notice that callee-save registers 120 * are BLOWN here; they have already been captured in the vframeArray at the time the return PC 121 * was patched. 122 */ 123 @Snippet 124 private static void uncommonTrapHandler(@ConstantParameter Register threadRegister, @ConstantParameter Register stackPointerRegister) { 125 final Word thread = registerAsWord(threadRegister); 126 final long registerSaver = SaveAllRegistersNode.saveAllRegisters(); 127 128 final int actionAndReason = readPendingDeoptimization(thread); 129 writePendingDeoptimization(thread, -1); 130 131 final Word unrollBlock = UncommonTrapCallNode.uncommonTrap(registerSaver, actionAndReason); 132 133 // Pop all the frames we must move/replace. 134 // 135 // Frame picture (youngest to oldest) 136 // 1: self-frame 137 // 2: deoptimizing frame 138 // 3: caller of deoptimizing frame (could be compiled/interpreted). 139 140 // Pop self-frame. 141 LeaveCurrentStackFrameNode.leaveCurrentStackFrame(registerSaver); 142 143 // Load the initial info we should save (e.g. frame pointer). 144 final Word initialInfo = unrollBlock.readWord(deoptimizationUnrollBlockInitialInfoOffset()); 145 146 // Pop deoptimized frame. 147 final int sizeOfDeoptimizedFrame = unrollBlock.readInt(deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset()); 148 LeaveDeoptimizedStackFrameNode.leaveDeoptimizedStackFrame(sizeOfDeoptimizedFrame, initialInfo); 149 150 /* 151 * Stack bang to make sure there's enough room for the interpreter frames. Bang stack for 152 * total size of the interpreter frames plus shadow page size. Bang one page at a time 153 * because large sizes can bang beyond yellow and red zones. 154 * 155 * @deprecated This code should go away as soon as JDK-8032410 hits the Graal repository. 156 */ 157 final int totalFrameSizes = unrollBlock.readInt(deoptimizationUnrollBlockTotalFrameSizesOffset()); 158 final int bangPages = NumUtil.roundUp(totalFrameSizes, pageSize()) / pageSize() + stackShadowPages(); 159 Word stackPointer = readRegister(stackPointerRegister); 160 161 for (int i = 1; i < bangPages; i++) { 162 stackPointer.writeInt((-i * pageSize()) + stackBias(), 0, STACK_BANG_LOCATION); 163 } 164 165 // Load number of interpreter frames. 166 final int numberOfFrames = unrollBlock.readInt(deoptimizationUnrollBlockNumberOfFramesOffset()); 167 168 // Load address of array of frame sizes. 169 final Word frameSizes = unrollBlock.readWord(deoptimizationUnrollBlockFrameSizesOffset()); 170 171 // Load address of array of frame PCs. 172 final Word framePcs = unrollBlock.readWord(deoptimizationUnrollBlockFramePcsOffset()); 173 174 /* 175 * Get the current stack pointer (sender's original SP) before adjustment so that we can 176 * save it in the skeletal interpreter frame. 177 */ 178 Word senderSp = readRegister(stackPointerRegister); 179 180 // Adjust old interpreter frame to make space for new frame's extra Java locals. 181 final int callerAdjustment = unrollBlock.readInt(deoptimizationUnrollBlockCallerAdjustmentOffset()); 182 writeRegister(stackPointerRegister, readRegister(stackPointerRegister).subtract(callerAdjustment)); 183 184 for (int i = 0; i < numberOfFrames; i++) { 185 final Word frameSize = frameSizes.readWord(i * wordSize()); 186 final Word framePc = framePcs.readWord(i * wordSize()); 187 188 // Push an interpreter frame onto the stack. 189 PushInterpreterFrameNode.pushInterpreterFrame(frameSize, framePc, senderSp, initialInfo); 190 191 // Get the current stack pointer (sender SP) and pass it to next frame. 192 senderSp = readRegister(stackPointerRegister); 193 } 194 195 // Get final return address. 196 final Word framePc = framePcs.readWord(numberOfFrames * wordSize()); 197 198 /* 199 * Enter a frame to call out to unpack frames. Since we changed the stack pointer to an 200 * unknown alignment we need to align it here before calling C++ code. 201 */ 202 final Word senderFp = initialInfo; 203 EnterUnpackFramesStackFrameNode.enterUnpackFramesStackFrame(framePc, senderSp, senderFp, registerSaver); 204 205 // Pass uncommon trap mode to unpack frames. 206 final int mode = deoptimizationUnpackUncommonTrap(); 207 unpackFrames(UNPACK_FRAMES, thread, mode); 208 209 LeaveUnpackFramesStackFrameNode.leaveUnpackFramesStackFrame(registerSaver); 210 } 211 212 /** 213 * Reads the value of the passed register as a Word. 214 */ 215 private static Word readRegister(Register register) { 216 return registerAsWord(register, false, false); 217 } 218 219 /** 220 * Writes the value of the passed register. 221 * 222 * @param value value the register should be set to 223 */ 224 private static void writeRegister(Register register, Word value) { 225 writeRegisterAsWord(register, value); 226 } 227 228 @Fold 229 private static int stackShadowPages() { 230 return config().useStackBanging ? config().stackShadowPages : 0; 231 } 232 233 /** 234 * Returns the stack bias for the host architecture. 235 * 236 * @deprecated This method should go away as soon as JDK-8032410 hits the Graal repository. 237 * 238 * @return stack bias 239 */ 240 @Deprecated 241 @Fold 242 private static int stackBias() { 243 return config().stackBias; 244 } 245 246 @Fold 247 private static int deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset() { 248 return config().deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset; 249 } 250 251 @Fold 252 private static int deoptimizationUnrollBlockCallerAdjustmentOffset() { 253 return config().deoptimizationUnrollBlockCallerAdjustmentOffset; 254 } 255 256 @Fold 257 private static int deoptimizationUnrollBlockNumberOfFramesOffset() { 258 return config().deoptimizationUnrollBlockNumberOfFramesOffset; 259 } 260 261 @Fold 262 private static int deoptimizationUnrollBlockTotalFrameSizesOffset() { 263 return config().deoptimizationUnrollBlockTotalFrameSizesOffset; 264 } 265 266 @Fold 267 private static int deoptimizationUnrollBlockFrameSizesOffset() { 268 return config().deoptimizationUnrollBlockFrameSizesOffset; 269 } 270 271 @Fold 272 private static int deoptimizationUnrollBlockFramePcsOffset() { 273 return config().deoptimizationUnrollBlockFramePcsOffset; 274 } 275 276 @Fold 277 private static int deoptimizationUnrollBlockInitialInfoOffset() { 278 return config().deoptimizationUnrollBlockInitialInfoOffset; 279 } 280 281 @Fold 282 private static int deoptimizationUnpackDeopt() { 283 return config().deoptimizationUnpackDeopt; 284 } 285 286 @Fold 287 private static int deoptimizationUnpackUncommonTrap() { 288 return config().deoptimizationUnpackUncommonTrap; 289 } 290 291 @NodeIntrinsic(value = StubForeignCallNode.class, setStampFromReturnType = true) 292 public static native int unpackFrames(@ConstantNodeParameter ForeignCallDescriptor unpackFrames, Word thread, int mode); 293}