1 /*
   2  * Copyright (c) 2013, 2016, 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.Options.PreferGraalStubs;
  27 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.readPendingDeoptimization;
  28 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.registerAsWord;
  29 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.writePendingDeoptimization;
  30 import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.writeRegisterAsWord;
  31 
  32 import org.graalvm.compiler.api.replacements.Fold;
  33 import org.graalvm.compiler.api.replacements.Fold.InjectedParameter;
  34 import org.graalvm.compiler.api.replacements.Snippet;
  35 import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter;
  36 import org.graalvm.compiler.core.common.LocationIdentity;
  37 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
  38 import org.graalvm.compiler.debug.GraalError;
  39 import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
  40 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
  41 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
  42 import org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage;
  43 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
  44 import org.graalvm.compiler.hotspot.nodes.SaveAllRegistersNode;
  45 import org.graalvm.compiler.hotspot.nodes.StubForeignCallNode;
  46 import org.graalvm.compiler.hotspot.nodes.UncommonTrapCallNode;
  47 import org.graalvm.compiler.nodes.NamedLocationIdentity;
  48 import org.graalvm.compiler.word.Word;
  49 
  50 import jdk.vm.ci.code.Register;
  51 import jdk.vm.ci.code.TargetDescription;
  52 
  53 /**
  54  * Uncommon trap stub.
  55  *
  56  * This is the entry point for code which is returning to a de-optimized frame.
  57  *
  58  * The steps taken by this frame are as follows:
  59  *
  60  * <li>push a dummy "register_save" and save the return values (O0, O1, F0/F1, G1) and all
  61  * potentially live registers (at a pollpoint many registers can be live).
  62  *
  63  * <li>call the C routine: Deoptimization::fetch_unroll_info (this function returns information
  64  * about the number and size of interpreter frames which are equivalent to the frame which is being
  65  * deoptimized)
  66  *
  67  * <li>deallocate the unpack frame, restoring only results values. Other volatile registers will now
  68  * be captured in the vframeArray as needed.
  69  *
  70  * <li>deallocate the deoptimization frame
  71  *
  72  * <li>in a loop using the information returned in the previous step push new interpreter frames
  73  * (take care to propagate the return values through each new frame pushed)
  74  *
  75  * <li>create a dummy "unpack_frame" and save the return values (O0, O1, F0)
  76  *
  77  * <li>call the C routine: Deoptimization::unpack_frames (this function lays out values on the
  78  * interpreter frame which was just created)
  79  *
  80  * <li>deallocate the dummy unpack_frame
  81  *
  82  * <li>ensure that all the return values are correctly set and then do a return to the interpreter
  83  * entry point
  84  *
  85  * <p>
  86  * <b>ATTENTION: We cannot do any complicated operations e.g. logging via printf in this snippet
  87  * because we change the current stack layout and so the code is very sensitive to register
  88  * allocation.</b>
  89  */
  90 public class UncommonTrapStub extends SnippetStub {
  91 
  92     public static final LocationIdentity STACK_BANG_LOCATION = NamedLocationIdentity.mutable("stack bang");
  93 
  94     private final TargetDescription target;
  95 
  96     public UncommonTrapStub(HotSpotProviders providers, TargetDescription target, HotSpotForeignCallLinkage linkage) {
  97         super(UncommonTrapStub.class, "uncommonTrapHandler", providers, linkage);
  98         this.target = target;
  99         assert PreferGraalStubs.getValue();
 100     }
 101 
 102     @Override
 103     public boolean preservesRegisters() {
 104         return false;
 105     }
 106 
 107     @Override
 108     protected Object getConstantParameterValue(int index, String name) {
 109         switch (index) {
 110             case 0:
 111                 return providers.getRegisters().getThreadRegister();
 112             case 1:
 113                 return providers.getRegisters().getStackPointerRegister();
 114             default:
 115                 throw GraalError.shouldNotReachHere("unknown parameter " + name + " at index " + index);
 116         }
 117     }
 118 
 119     /**
 120      * Uncommon trap handler.
 121      *
 122      * We save the argument return registers. We call the first C routine, fetch_unroll_info(). This
 123      * routine captures the return values and returns a structure which describes the current frame
 124      * size and the sizes of all replacement frames. The current frame is compiled code and may
 125      * contain many inlined functions, each with their own JVM state. We pop the current frame, then
 126      * push all the new frames. Then we call the C routine unpack_frames() to populate these frames.
 127      * Finally unpack_frames() returns us the new target address. Notice that callee-save registers
 128      * are BLOWN here; they have already been captured in the vframeArray at the time the return PC
 129      * was patched.
 130      */
 131     @Snippet
 132     private static void uncommonTrapHandler(@ConstantParameter Register threadRegister, @ConstantParameter Register stackPointerRegister) {
 133         final Word thread = registerAsWord(threadRegister);
 134         final long registerSaver = SaveAllRegistersNode.saveAllRegisters();
 135 
 136         final int actionAndReason = readPendingDeoptimization(thread);
 137         writePendingDeoptimization(thread, -1);
 138 
 139         final Word unrollBlock = UncommonTrapCallNode.uncommonTrap(registerSaver, actionAndReason, deoptimizationUnpackUncommonTrap(INJECTED_VMCONFIG));
 140 
 141         DeoptimizationStub.deoptimizationCommon(stackPointerRegister, thread, registerSaver, unrollBlock);
 142     }
 143 
 144     /**
 145      * Reads the value of the passed register as a Word.
 146      */
 147     private static Word readRegister(Register register) {
 148         return registerAsWord(register, false, false);
 149     }
 150 
 151     /**
 152      * Writes the value of the passed register.
 153      *
 154      * @param value value the register should be set to
 155      */
 156     private static void writeRegister(Register register, Word value) {
 157         writeRegisterAsWord(register, value);
 158     }
 159 
 160     @Fold
 161     static int stackShadowPages(@InjectedParameter GraalHotSpotVMConfig config) {
 162         return config.useStackBanging ? config.stackShadowPages : 0;
 163     }
 164 
 165     /**
 166      * Returns the stack bias for the host architecture.
 167      *
 168      * @deprecated This method should go away as soon as JDK-8032410 hits the Graal repository.
 169      *
 170      * @return stack bias
 171      */
 172     @Deprecated
 173     @Fold
 174     static int stackBias(@InjectedParameter GraalHotSpotVMConfig config) {
 175         return config.stackBias;
 176     }
 177 
 178     @Fold
 179     static int deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset(@InjectedParameter GraalHotSpotVMConfig config) {
 180         return config.deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset;
 181     }
 182 
 183     @Fold
 184     static int deoptimizationUnrollBlockCallerAdjustmentOffset(@InjectedParameter GraalHotSpotVMConfig config) {
 185         return config.deoptimizationUnrollBlockCallerAdjustmentOffset;
 186     }
 187 
 188     @Fold
 189     static int deoptimizationUnrollBlockNumberOfFramesOffset(@InjectedParameter GraalHotSpotVMConfig config) {
 190         return config.deoptimizationUnrollBlockNumberOfFramesOffset;
 191     }
 192 
 193     @Fold
 194     static int deoptimizationUnrollBlockTotalFrameSizesOffset(@InjectedParameter GraalHotSpotVMConfig config) {
 195         return config.deoptimizationUnrollBlockTotalFrameSizesOffset;
 196     }
 197 
 198     @Fold
 199     static int deoptimizationUnrollBlockFrameSizesOffset(@InjectedParameter GraalHotSpotVMConfig config) {
 200         return config.deoptimizationUnrollBlockFrameSizesOffset;
 201     }
 202 
 203     @Fold
 204     static int deoptimizationUnrollBlockFramePcsOffset(@InjectedParameter GraalHotSpotVMConfig config) {
 205         return config.deoptimizationUnrollBlockFramePcsOffset;
 206     }
 207 
 208     @Fold
 209     static int deoptimizationUnrollBlockInitialInfoOffset(@InjectedParameter GraalHotSpotVMConfig config) {
 210         return config.deoptimizationUnrollBlockInitialInfoOffset;
 211     }
 212 
 213     @Fold
 214     static int deoptimizationUnpackDeopt(@InjectedParameter GraalHotSpotVMConfig config) {
 215         return config.deoptimizationUnpackDeopt;
 216     }
 217 
 218     @Fold
 219     static int deoptimizationUnpackUncommonTrap(@InjectedParameter GraalHotSpotVMConfig config) {
 220         return config.deoptimizationUnpackUncommonTrap;
 221     }
 222 
 223     @NodeIntrinsic(value = StubForeignCallNode.class, setStampFromReturnType = true)
 224     public static native int unpackFrames(@ConstantNodeParameter ForeignCallDescriptor unpackFrames, Word thread, int mode);
 225 }