/*
 * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package org.graalvm.compiler.hotspot.stubs;

import static org.graalvm.compiler.hotspot.GraalHotSpotVMConfig.INJECTED_VMCONFIG;
import static org.graalvm.compiler.hotspot.HotSpotBackend.Options.PreferGraalStubs;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.readPendingDeoptimization;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.registerAsWord;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.writePendingDeoptimization;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.writeRegisterAsWord;

import org.graalvm.compiler.api.replacements.Fold;
import org.graalvm.compiler.api.replacements.Fold.InjectedParameter;
import org.graalvm.compiler.api.replacements.Snippet;
import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter;
import org.graalvm.compiler.core.common.LocationIdentity;
import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
import org.graalvm.compiler.graph.Node.NodeIntrinsic;
import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
import org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage;
import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
import org.graalvm.compiler.hotspot.nodes.SaveAllRegistersNode;
import org.graalvm.compiler.hotspot.nodes.StubForeignCallNode;
import org.graalvm.compiler.hotspot.nodes.UncommonTrapCallNode;
import org.graalvm.compiler.nodes.NamedLocationIdentity;
import org.graalvm.compiler.word.Word;

import jdk.vm.ci.code.Register;
import jdk.vm.ci.code.TargetDescription;

Uncommon trap stub. This is the entry point for code which is returning to a de-optimized frame. The steps taken by this frame are as follows:
  • push a dummy "register_save" and save the return values (O0, O1, F0/F1, G1) and all potentially live registers (at a pollpoint many registers can be live).
  • call the C routine: Deoptimization::fetch_unroll_info (this function returns information about the number and size of interpreter frames which are equivalent to the frame which is being deoptimized)
  • deallocate the unpack frame, restoring only results values. Other volatile registers will now be captured in the vframeArray as needed.
  • deallocate the deoptimization frame
  • in a loop using the information returned in the previous step push new interpreter frames (take care to propagate the return values through each new frame pushed)
  • create a dummy "unpack_frame" and save the return values (O0, O1, F0)
  • call the C routine: Deoptimization::unpack_frames (this function lays out values on the interpreter frame which was just created)
  • deallocate the dummy unpack_frame
  • ensure that all the return values are correctly set and then do a return to the interpreter entry point

    ATTENTION: We cannot do any complicated operations e.g. logging via printf in this snippet because we change the current stack layout and so the code is very sensitive to register allocation.

  • /** * Uncommon trap stub. * * This is the entry point for code which is returning to a de-optimized frame. * * The steps taken by this frame are as follows: * * <li>push a dummy "register_save" and save the return values (O0, O1, F0/F1, G1) and all * potentially live registers (at a pollpoint many registers can be live). * * <li>call the C routine: Deoptimization::fetch_unroll_info (this function returns information * about the number and size of interpreter frames which are equivalent to the frame which is being * deoptimized) * * <li>deallocate the unpack frame, restoring only results values. Other volatile registers will now * be captured in the vframeArray as needed. * * <li>deallocate the deoptimization frame * * <li>in a loop using the information returned in the previous step push new interpreter frames * (take care to propagate the return values through each new frame pushed) * * <li>create a dummy "unpack_frame" and save the return values (O0, O1, F0) * * <li>call the C routine: Deoptimization::unpack_frames (this function lays out values on the * interpreter frame which was just created) * * <li>deallocate the dummy unpack_frame * * <li>ensure that all the return values are correctly set and then do a return to the interpreter * entry point * * <p> * <b>ATTENTION: We cannot do any complicated operations e.g. logging via printf in this snippet * because we change the current stack layout and so the code is very sensitive to register * allocation.</b> */
    public class UncommonTrapStub extends SnippetStub { public static final LocationIdentity STACK_BANG_LOCATION = NamedLocationIdentity.mutable("stack bang"); private final TargetDescription target; public UncommonTrapStub(HotSpotProviders providers, TargetDescription target, HotSpotForeignCallLinkage linkage) { super(UncommonTrapStub.class, "uncommonTrapHandler", providers, linkage); this.target = target; assert PreferGraalStubs.getValue(); } @Override public boolean preservesRegisters() { return false; } @Override protected Object getConstantParameterValue(int index, String name) { switch (index) { case 0: return providers.getRegisters().getThreadRegister(); case 1: return providers.getRegisters().getStackPointerRegister(); default: throw GraalError.shouldNotReachHere("unknown parameter " + name + " at index " + index); } }
    Uncommon trap handler. We save the argument return registers. We call the first C routine, fetch_unroll_info(). This routine captures the return values and returns a structure which describes the current frame size and the sizes of all replacement frames. The current frame is compiled code and may contain many inlined functions, each with their own JVM state. We pop the current frame, then push all the new frames. Then we call the C routine unpack_frames() to populate these frames. Finally unpack_frames() returns us the new target address. Notice that callee-save registers are BLOWN here; they have already been captured in the vframeArray at the time the return PC was patched.
    /** * Uncommon trap handler. * * We save the argument return registers. We call the first C routine, fetch_unroll_info(). This * routine captures the return values and returns a structure which describes the current frame * size and the sizes of all replacement frames. The current frame is compiled code and may * contain many inlined functions, each with their own JVM state. We pop the current frame, then * push all the new frames. Then we call the C routine unpack_frames() to populate these frames. * Finally unpack_frames() returns us the new target address. Notice that callee-save registers * are BLOWN here; they have already been captured in the vframeArray at the time the return PC * was patched. */
    @Snippet private static void uncommonTrapHandler(@ConstantParameter Register threadRegister, @ConstantParameter Register stackPointerRegister) { final Word thread = registerAsWord(threadRegister); final long registerSaver = SaveAllRegistersNode.saveAllRegisters(); final int actionAndReason = readPendingDeoptimization(thread); writePendingDeoptimization(thread, -1); final Word unrollBlock = UncommonTrapCallNode.uncommonTrap(registerSaver, actionAndReason, deoptimizationUnpackUncommonTrap(INJECTED_VMCONFIG)); DeoptimizationStub.deoptimizationCommon(stackPointerRegister, thread, registerSaver, unrollBlock); }
    Reads the value of the passed register as a Word.
    /** * Reads the value of the passed register as a Word. */
    private static Word readRegister(Register register) { return registerAsWord(register, false, false); }
    Writes the value of the passed register.
    Params:
    • value – value the register should be set to
    /** * Writes the value of the passed register. * * @param value value the register should be set to */
    private static void writeRegister(Register register, Word value) { writeRegisterAsWord(register, value); } @Fold static int stackShadowPages(@InjectedParameter GraalHotSpotVMConfig config) { return config.useStackBanging ? config.stackShadowPages : 0; }
    Returns the stack bias for the host architecture.
    Deprecated:This method should go away as soon as JDK-8032410 hits the Graal repository.
    Returns:stack bias
    /** * Returns the stack bias for the host architecture. * * @deprecated This method should go away as soon as JDK-8032410 hits the Graal repository. * * @return stack bias */
    @Deprecated @Fold static int stackBias(@InjectedParameter GraalHotSpotVMConfig config) { return config.stackBias; } @Fold static int deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset(@InjectedParameter GraalHotSpotVMConfig config) { return config.deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset; } @Fold static int deoptimizationUnrollBlockCallerAdjustmentOffset(@InjectedParameter GraalHotSpotVMConfig config) { return config.deoptimizationUnrollBlockCallerAdjustmentOffset; } @Fold static int deoptimizationUnrollBlockNumberOfFramesOffset(@InjectedParameter GraalHotSpotVMConfig config) { return config.deoptimizationUnrollBlockNumberOfFramesOffset; } @Fold static int deoptimizationUnrollBlockTotalFrameSizesOffset(@InjectedParameter GraalHotSpotVMConfig config) { return config.deoptimizationUnrollBlockTotalFrameSizesOffset; } @Fold static int deoptimizationUnrollBlockFrameSizesOffset(@InjectedParameter GraalHotSpotVMConfig config) { return config.deoptimizationUnrollBlockFrameSizesOffset; } @Fold static int deoptimizationUnrollBlockFramePcsOffset(@InjectedParameter GraalHotSpotVMConfig config) { return config.deoptimizationUnrollBlockFramePcsOffset; } @Fold static int deoptimizationUnrollBlockInitialInfoOffset(@InjectedParameter GraalHotSpotVMConfig config) { return config.deoptimizationUnrollBlockInitialInfoOffset; } @Fold static int deoptimizationUnpackDeopt(@InjectedParameter GraalHotSpotVMConfig config) { return config.deoptimizationUnpackDeopt; } @Fold static int deoptimizationUnpackUncommonTrap(@InjectedParameter GraalHotSpotVMConfig config) { return config.deoptimizationUnpackUncommonTrap; } @NodeIntrinsic(value = StubForeignCallNode.class, setStampFromReturnType = true) public static native int unpackFrames(@ConstantNodeParameter ForeignCallDescriptor unpackFrames, Word thread, int mode); }