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 }