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}