1 /* 2 * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.graalvm.compiler.hotspot.stubs; 24 25 import static org.graalvm.compiler.hotspot.GraalHotSpotVMConfig.INJECTED_VMCONFIG; 26 import static org.graalvm.compiler.hotspot.HotSpotBackend.UNPACK_FRAMES; 27 import static org.graalvm.compiler.hotspot.HotSpotBackend.Options.PreferGraalStubs; 28 import static org.graalvm.compiler.hotspot.nodes.DeoptimizationFetchUnrollInfoCallNode.fetchUnrollInfo; 29 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.pageSize; 30 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.registerAsWord; 31 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.wordSize; 32 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.writeRegisterAsWord; 33 import static org.graalvm.compiler.hotspot.stubs.UncommonTrapStub.STACK_BANG_LOCATION; 34 35 import org.graalvm.compiler.api.replacements.Fold; 36 import org.graalvm.compiler.api.replacements.Fold.InjectedParameter; 37 import org.graalvm.compiler.api.replacements.Snippet; 38 import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter; 39 import org.graalvm.compiler.asm.NumUtil; 40 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor; 41 import org.graalvm.compiler.debug.GraalError; 42 import org.graalvm.compiler.graph.Node.ConstantNodeParameter; 43 import org.graalvm.compiler.graph.Node.NodeIntrinsic; 44 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; 45 import org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage; 46 import org.graalvm.compiler.hotspot.meta.HotSpotProviders; 47 import org.graalvm.compiler.hotspot.nodes.EnterUnpackFramesStackFrameNode; 48 import org.graalvm.compiler.hotspot.nodes.LeaveCurrentStackFrameNode; 49 import org.graalvm.compiler.hotspot.nodes.LeaveDeoptimizedStackFrameNode; 50 import org.graalvm.compiler.hotspot.nodes.LeaveUnpackFramesStackFrameNode; 51 import org.graalvm.compiler.hotspot.nodes.PushInterpreterFrameNode; 52 import org.graalvm.compiler.hotspot.nodes.SaveAllRegistersNode; 53 import org.graalvm.compiler.hotspot.nodes.StubForeignCallNode; 54 import org.graalvm.compiler.word.Word; 55 56 import jdk.vm.ci.code.Register; 57 import jdk.vm.ci.code.TargetDescription; 58 59 /** 60 * Deoptimization stub. 61 * 62 * This is the entry point for code which is returning to a de-optimized frame. 63 * 64 * The steps taken by this frame are as follows: 65 * 66 * <li>push a dummy "register_save" and save the return values (O0, O1, F0/F1, G1) and all 67 * potentially live registers (at a pollpoint many registers can be live). 68 * 69 * <li>call the C routine: Deoptimization::fetch_unroll_info (this function returns information 70 * about the number and size of interpreter frames which are equivalent to the frame which is being 71 * deoptimized) 72 * 73 * <li>deallocate the unpack frame, restoring only results values. Other volatile registers will now 74 * be captured in the vframeArray as needed. 75 * 76 * <li>deallocate the deoptimization frame 77 * 78 * <li>in a loop using the information returned in the previous step push new interpreter frames 79 * (take care to propagate the return values through each new frame pushed) 80 * 81 * <li>create a dummy "unpack_frame" and save the return values (O0, O1, F0) 82 * 83 * <li>call the C routine: Deoptimization::unpack_frames (this function lays out values on the 84 * interpreter frame which was just created) 85 * 86 * <li>deallocate the dummy unpack_frame 87 * 88 * <li>ensure that all the return values are correctly set and then do a return to the interpreter 89 * entry point 90 * 91 * <p> 92 * <b>ATTENTION: We cannot do any complicated operations e.g. logging via printf in this snippet 93 * because we change the current stack layout and so the code is very sensitive to register 94 * allocation.</b> 95 */ 96 public class DeoptimizationStub extends SnippetStub { 97 98 private final TargetDescription target; 99 100 public DeoptimizationStub(HotSpotProviders providers, TargetDescription target, HotSpotForeignCallLinkage linkage) { 101 super(DeoptimizationStub.class, "deoptimizationHandler", providers, linkage); 102 this.target = target; 103 assert PreferGraalStubs.getValue(); 104 } 105 106 @Override 107 public boolean preservesRegisters() { 108 return false; 109 } 110 111 @Override 112 protected Object getConstantParameterValue(int index, String name) { 113 switch (index) { 114 case 0: 115 return providers.getRegisters().getThreadRegister(); 116 case 1: 117 return providers.getRegisters().getStackPointerRegister(); 118 default: 119 throw GraalError.shouldNotReachHere("unknown parameter " + name + " at index " + index); 120 } 121 } 122 123 /** 124 * Deoptimization handler for normal deoptimization 125 * {@link GraalHotSpotVMConfig#deoptimizationUnpackDeopt}. 126 */ 127 @Snippet 128 private static void deoptimizationHandler(@ConstantParameter Register threadRegister, @ConstantParameter Register stackPointerRegister) { 129 final Word thread = registerAsWord(threadRegister); 130 final long registerSaver = SaveAllRegistersNode.saveAllRegisters(); 131 132 final Word unrollBlock = fetchUnrollInfo(registerSaver, deoptimizationUnpackDeopt(INJECTED_VMCONFIG)); 133 134 deoptimizationCommon(stackPointerRegister, thread, registerSaver, unrollBlock); 135 } 136 137 static void deoptimizationCommon(Register stackPointerRegister, final Word thread, final long registerSaver, final Word unrollBlock) { 138 // Pop all the frames we must move/replace. 139 // 140 // Frame picture (youngest to oldest) 141 // 1: self-frame 142 // 2: deoptimizing frame 143 // 3: caller of deoptimizing frame (could be compiled/interpreted). 144 145 // Pop self-frame. 146 LeaveCurrentStackFrameNode.leaveCurrentStackFrame(registerSaver); 147 148 // Load the initial info we should save (e.g. frame pointer). 149 final Word initialInfo = unrollBlock.readWord(deoptimizationUnrollBlockInitialInfoOffset(INJECTED_VMCONFIG)); 150 151 // Pop deoptimized frame. 152 final int sizeOfDeoptimizedFrame = unrollBlock.readInt(deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset(INJECTED_VMCONFIG)); 153 LeaveDeoptimizedStackFrameNode.leaveDeoptimizedStackFrame(sizeOfDeoptimizedFrame, initialInfo); 154 155 /* 156 * Stack bang to make sure there's enough room for the interpreter frames. Bang stack for 157 * total size of the interpreter frames plus shadow page size. Bang one page at a time 158 * because large sizes can bang beyond yellow and red zones. 159 * 160 * @deprecated This code should go away as soon as JDK-8032410 hits the Graal repository. 161 */ 162 final int totalFrameSizes = unrollBlock.readInt(deoptimizationUnrollBlockTotalFrameSizesOffset(INJECTED_VMCONFIG)); 163 final int bangPages = NumUtil.roundUp(totalFrameSizes, pageSize()) / pageSize() + stackShadowPages(INJECTED_VMCONFIG); 164 Word stackPointer = readRegister(stackPointerRegister); 165 166 for (int i = 1; i < bangPages; i++) { 167 stackPointer.writeInt((-i * pageSize()) + stackBias(INJECTED_VMCONFIG), 0, STACK_BANG_LOCATION); 168 } 169 170 // Load number of interpreter frames. 171 final int numberOfFrames = unrollBlock.readInt(deoptimizationUnrollBlockNumberOfFramesOffset(INJECTED_VMCONFIG)); 172 173 // Load address of array of frame sizes. 174 final Word frameSizes = unrollBlock.readWord(deoptimizationUnrollBlockFrameSizesOffset(INJECTED_VMCONFIG)); 175 176 // Load address of array of frame PCs. 177 final Word framePcs = unrollBlock.readWord(deoptimizationUnrollBlockFramePcsOffset(INJECTED_VMCONFIG)); 178 179 /* 180 * Get the current stack pointer (sender's original SP) before adjustment so that we can 181 * save it in the skeletal interpreter frame. 182 */ 183 Word senderSp = readRegister(stackPointerRegister); 184 185 // Adjust old interpreter frame to make space for new frame's extra Java locals. 186 final int callerAdjustment = unrollBlock.readInt(deoptimizationUnrollBlockCallerAdjustmentOffset(INJECTED_VMCONFIG)); 187 writeRegister(stackPointerRegister, readRegister(stackPointerRegister).subtract(callerAdjustment)); 188 189 for (int i = 0; i < numberOfFrames; i++) { 190 final Word frameSize = frameSizes.readWord(i * wordSize()); 191 final Word framePc = framePcs.readWord(i * wordSize()); 192 193 // Push an interpreter frame onto the stack. 194 PushInterpreterFrameNode.pushInterpreterFrame(frameSize, framePc, senderSp, initialInfo); 195 196 // Get the current stack pointer (sender SP) and pass it to next frame. 197 senderSp = readRegister(stackPointerRegister); 198 } 199 200 // Get final return address. 201 final Word framePc = framePcs.readWord(numberOfFrames * wordSize()); 202 203 /* 204 * Enter a frame to call out to unpack frames. Since we changed the stack pointer to an 205 * unknown alignment we need to align it here before calling C++ code. 206 */ 207 final Word senderFp = initialInfo; 208 EnterUnpackFramesStackFrameNode.enterUnpackFramesStackFrame(framePc, senderSp, senderFp, registerSaver); 209 210 final int mode = unrollBlock.readInt(deoptimizationUnrollBlockUnpackKindOffset(INJECTED_VMCONFIG)); 211 unpackFrames(UNPACK_FRAMES, thread, mode); 212 213 LeaveUnpackFramesStackFrameNode.leaveUnpackFramesStackFrame(registerSaver); 214 } 215 216 /** 217 * Reads the value of the passed register as a Word. 218 */ 219 private static Word readRegister(Register register) { 220 return registerAsWord(register, false, false); 221 } 222 223 /** 224 * Writes the value of the passed register. 225 * 226 * @param value value the register should be set to 227 */ 228 private static void writeRegister(Register register, Word value) { 229 writeRegisterAsWord(register, value); 230 } 231 232 @Fold 233 static int stackShadowPages(@InjectedParameter GraalHotSpotVMConfig config) { 234 return config.useStackBanging ? config.stackShadowPages : 0; 235 } 236 237 /** 238 * Returns the stack bias for the host architecture. 239 * 240 * @deprecated This method should go away as soon as JDK-8032410 hits the Graal repository. 241 * 242 * @return stack bias 243 */ 244 @Deprecated 245 @Fold 246 static int stackBias(@InjectedParameter GraalHotSpotVMConfig config) { 247 return config.stackBias; 248 } 249 250 @Fold 251 static int deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset(@InjectedParameter GraalHotSpotVMConfig config) { 252 return config.deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset; 253 } 254 255 @Fold 256 static int deoptimizationUnrollBlockCallerAdjustmentOffset(@InjectedParameter GraalHotSpotVMConfig config) { 257 return config.deoptimizationUnrollBlockCallerAdjustmentOffset; 258 } 259 260 @Fold 261 static int deoptimizationUnrollBlockNumberOfFramesOffset(@InjectedParameter GraalHotSpotVMConfig config) { 262 return config.deoptimizationUnrollBlockNumberOfFramesOffset; 263 } 264 265 @Fold 266 static int deoptimizationUnrollBlockTotalFrameSizesOffset(@InjectedParameter GraalHotSpotVMConfig config) { 267 return config.deoptimizationUnrollBlockTotalFrameSizesOffset; 268 } 269 270 @Fold 271 static int deoptimizationUnrollBlockUnpackKindOffset(@InjectedParameter GraalHotSpotVMConfig config) { 272 return config.deoptimizationUnrollBlockUnpackKindOffset; 273 } 274 275 @Fold 276 static int deoptimizationUnrollBlockFrameSizesOffset(@InjectedParameter GraalHotSpotVMConfig config) { 277 return config.deoptimizationUnrollBlockFrameSizesOffset; 278 } 279 280 @Fold 281 static int deoptimizationUnrollBlockFramePcsOffset(@InjectedParameter GraalHotSpotVMConfig config) { 282 return config.deoptimizationUnrollBlockFramePcsOffset; 283 } 284 285 @Fold 286 static int deoptimizationUnrollBlockInitialInfoOffset(@InjectedParameter GraalHotSpotVMConfig config) { 287 return config.deoptimizationUnrollBlockInitialInfoOffset; 288 } 289 290 @Fold 291 static int deoptimizationUnpackDeopt(@InjectedParameter GraalHotSpotVMConfig config) { 292 return config.deoptimizationUnpackDeopt; 293 } 294 295 @Fold 296 static int deoptimizationUnpackUncommonTrap(@InjectedParameter GraalHotSpotVMConfig config) { 297 return config.deoptimizationUnpackUncommonTrap; 298 } 299 300 @NodeIntrinsic(value = StubForeignCallNode.class, setStampFromReturnType = true) 301 public static native int unpackFrames(@ConstantNodeParameter ForeignCallDescriptor unpackFrames, Word thread, int mode); 302 }