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 jdk.internal.jvmci.code.*; 026import jdk.internal.jvmci.common.*; 027import jdk.internal.jvmci.hotspot.*; 028import static com.oracle.graal.hotspot.HotSpotBackend.*; 029import static com.oracle.graal.hotspot.HotSpotBackend.Options.*; 030import static com.oracle.graal.hotspot.nodes.DeoptimizationFetchUnrollInfoCallNode.*; 031import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*; 032import static com.oracle.graal.hotspot.stubs.UncommonTrapStub.*; 033 034import com.oracle.graal.api.replacements.*; 035import com.oracle.graal.asm.*; 036import com.oracle.graal.compiler.common.spi.*; 037import com.oracle.graal.graph.Node.ConstantNodeParameter; 038import com.oracle.graal.graph.Node.NodeIntrinsic; 039import com.oracle.graal.hotspot.*; 040import com.oracle.graal.hotspot.meta.*; 041import com.oracle.graal.hotspot.nodes.*; 042import com.oracle.graal.replacements.*; 043import com.oracle.graal.replacements.Snippet.ConstantParameter; 044import com.oracle.graal.word.*; 045 046/** 047 * Deoptimization stub. 048 * 049 * This is the entry point for code which is returning to a de-optimized frame. 050 * 051 * The steps taken by this frame are as follows: 052 * 053 * <li>push a dummy "register_save" and save the return values (O0, O1, F0/F1, G1) and all 054 * potentially live registers (at a pollpoint many registers can be live). 055 * 056 * <li>call the C routine: Deoptimization::fetch_unroll_info (this function returns information 057 * about the number and size of interpreter frames which are equivalent to the frame which is being 058 * deoptimized) 059 * 060 * <li>deallocate the unpack frame, restoring only results values. Other volatile registers will now 061 * be captured in the vframeArray as needed. 062 * 063 * <li>deallocate the deoptimization frame 064 * 065 * <li>in a loop using the information returned in the previous step push new interpreter frames 066 * (take care to propagate the return values through each new frame pushed) 067 * 068 * <li>create a dummy "unpack_frame" and save the return values (O0, O1, F0) 069 * 070 * <li>call the C routine: Deoptimization::unpack_frames (this function lays out values on the 071 * interpreter frame which was just created) 072 * 073 * <li>deallocate the dummy unpack_frame 074 * 075 * <li>ensure that all the return values are correctly set and then do a return to the interpreter 076 * entry point 077 * 078 * <p> 079 * <b>ATTENTION: We cannot do any complicated operations e.g. logging via printf in this snippet 080 * because we change the current stack layout and so the code is very sensitive to register 081 * allocation.</b> 082 */ 083public class DeoptimizationStub extends SnippetStub { 084 085 private final TargetDescription target; 086 087 public DeoptimizationStub(HotSpotProviders providers, TargetDescription target, HotSpotForeignCallLinkage linkage) { 088 super(DeoptimizationStub.class, "deoptimizationHandler", providers, linkage); 089 this.target = target; 090 assert PreferGraalStubs.getValue(); 091 } 092 093 @Override 094 public boolean preservesRegisters() { 095 return false; 096 } 097 098 @Override 099 protected Object getConstantParameterValue(int index, String name) { 100 switch (index) { 101 case 0: 102 return providers.getRegisters().getThreadRegister(); 103 case 1: 104 return providers.getRegisters().getStackPointerRegister(); 105 default: 106 throw JVMCIError.shouldNotReachHere("unknown parameter " + name + " at index " + index); 107 } 108 } 109 110 /** 111 * Deoptimization handler for normal deoptimization 112 * {@link HotSpotVMConfig#deoptimizationUnpackDeopt}. 113 */ 114 @Snippet 115 private static void deoptimizationHandler(@ConstantParameter Register threadRegister, @ConstantParameter Register stackPointerRegister) { 116 final Word thread = registerAsWord(threadRegister); 117 final long registerSaver = SaveAllRegistersNode.saveAllRegisters(); 118 119 final Word unrollBlock = fetchUnrollInfo(registerSaver); 120 121 // Pop all the frames we must move/replace. 122 // 123 // Frame picture (youngest to oldest) 124 // 1: self-frame 125 // 2: deoptimizing frame 126 // 3: caller of deoptimizing frame (could be compiled/interpreted). 127 128 // Pop self-frame. 129 LeaveCurrentStackFrameNode.leaveCurrentStackFrame(registerSaver); 130 131 // Load the initial info we should save (e.g. frame pointer). 132 final Word initialInfo = unrollBlock.readWord(deoptimizationUnrollBlockInitialInfoOffset()); 133 134 // Pop deoptimized frame. 135 final int sizeOfDeoptimizedFrame = unrollBlock.readInt(deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset()); 136 LeaveDeoptimizedStackFrameNode.leaveDeoptimizedStackFrame(sizeOfDeoptimizedFrame, initialInfo); 137 138 /* 139 * Stack bang to make sure there's enough room for the interpreter frames. Bang stack for 140 * total size of the interpreter frames plus shadow page size. Bang one page at a time 141 * because large sizes can bang beyond yellow and red zones. 142 * 143 * @deprecated This code should go away as soon as JDK-8032410 hits the Graal repository. 144 */ 145 final int totalFrameSizes = unrollBlock.readInt(deoptimizationUnrollBlockTotalFrameSizesOffset()); 146 final int bangPages = NumUtil.roundUp(totalFrameSizes, pageSize()) / pageSize() + stackShadowPages(); 147 Word stackPointer = readRegister(stackPointerRegister); 148 149 for (int i = 1; i < bangPages; i++) { 150 stackPointer.writeInt((-i * pageSize()) + stackBias(), 0, STACK_BANG_LOCATION); 151 } 152 153 // Load number of interpreter frames. 154 final int numberOfFrames = unrollBlock.readInt(deoptimizationUnrollBlockNumberOfFramesOffset()); 155 156 // Load address of array of frame sizes. 157 final Word frameSizes = unrollBlock.readWord(deoptimizationUnrollBlockFrameSizesOffset()); 158 159 // Load address of array of frame PCs. 160 final Word framePcs = unrollBlock.readWord(deoptimizationUnrollBlockFramePcsOffset()); 161 162 /* 163 * Get the current stack pointer (sender's original SP) before adjustment so that we can 164 * save it in the skeletal interpreter frame. 165 */ 166 Word senderSp = readRegister(stackPointerRegister); 167 168 // Adjust old interpreter frame to make space for new frame's extra Java locals. 169 final int callerAdjustment = unrollBlock.readInt(deoptimizationUnrollBlockCallerAdjustmentOffset()); 170 writeRegister(stackPointerRegister, readRegister(stackPointerRegister).subtract(callerAdjustment)); 171 172 for (int i = 0; i < numberOfFrames; i++) { 173 final Word frameSize = frameSizes.readWord(i * wordSize()); 174 final Word framePc = framePcs.readWord(i * wordSize()); 175 176 // Push an interpreter frame onto the stack. 177 PushInterpreterFrameNode.pushInterpreterFrame(frameSize, framePc, senderSp, initialInfo); 178 179 // Get the current stack pointer (sender SP) and pass it to next frame. 180 senderSp = readRegister(stackPointerRegister); 181 } 182 183 // Get final return address. 184 final Word framePc = framePcs.readWord(numberOfFrames * wordSize()); 185 186 /* 187 * Enter a frame to call out to unpack frames. Since we changed the stack pointer to an 188 * unknown alignment we need to align it here before calling C++ code. 189 */ 190 final Word senderFp = initialInfo; 191 EnterUnpackFramesStackFrameNode.enterUnpackFramesStackFrame(framePc, senderSp, senderFp, registerSaver); 192 193 // Pass unpack deopt mode to unpack frames. 194 final int mode = deoptimizationUnpackDeopt(); 195 unpackFrames(UNPACK_FRAMES, thread, mode); 196 197 LeaveUnpackFramesStackFrameNode.leaveUnpackFramesStackFrame(registerSaver); 198 } 199 200 /** 201 * Reads the value of the passed register as a Word. 202 */ 203 private static Word readRegister(Register register) { 204 return registerAsWord(register, false, false); 205 } 206 207 /** 208 * Writes the value of the passed register. 209 * 210 * @param value value the register should be set to 211 */ 212 private static void writeRegister(Register register, Word value) { 213 writeRegisterAsWord(register, value); 214 } 215 216 @Fold 217 private static int stackShadowPages() { 218 return config().useStackBanging ? config().stackShadowPages : 0; 219 } 220 221 /** 222 * Returns the stack bias for the host architecture. 223 * 224 * @deprecated This method should go away as soon as JDK-8032410 hits the Graal repository. 225 * 226 * @return stack bias 227 */ 228 @Deprecated 229 @Fold 230 private static int stackBias() { 231 return config().stackBias; 232 } 233 234 @Fold 235 private static int deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset() { 236 return config().deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset; 237 } 238 239 @Fold 240 private static int deoptimizationUnrollBlockCallerAdjustmentOffset() { 241 return config().deoptimizationUnrollBlockCallerAdjustmentOffset; 242 } 243 244 @Fold 245 private static int deoptimizationUnrollBlockNumberOfFramesOffset() { 246 return config().deoptimizationUnrollBlockNumberOfFramesOffset; 247 } 248 249 @Fold 250 private static int deoptimizationUnrollBlockTotalFrameSizesOffset() { 251 return config().deoptimizationUnrollBlockTotalFrameSizesOffset; 252 } 253 254 @Fold 255 private static int deoptimizationUnrollBlockFrameSizesOffset() { 256 return config().deoptimizationUnrollBlockFrameSizesOffset; 257 } 258 259 @Fold 260 private static int deoptimizationUnrollBlockFramePcsOffset() { 261 return config().deoptimizationUnrollBlockFramePcsOffset; 262 } 263 264 @Fold 265 private static int deoptimizationUnrollBlockInitialInfoOffset() { 266 return config().deoptimizationUnrollBlockInitialInfoOffset; 267 } 268 269 @Fold 270 private static int deoptimizationUnpackDeopt() { 271 return config().deoptimizationUnpackDeopt; 272 } 273 274 @Fold 275 private static int deoptimizationUnpackUncommonTrap() { 276 return config().deoptimizationUnpackUncommonTrap; 277 } 278 279 @NodeIntrinsic(value = StubForeignCallNode.class, setStampFromReturnType = true) 280 public static native int unpackFrames(@ConstantNodeParameter ForeignCallDescriptor unpackFrames, Word thread, int mode); 281}