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 static com.oracle.graal.hotspot.HotSpotBackend.*;
026import static com.oracle.graal.hotspot.HotSpotBackend.Options.*;
027import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
028import jdk.internal.jvmci.code.*;
029import jdk.internal.jvmci.common.*;
030import jdk.internal.jvmci.meta.*;
031
032import com.oracle.graal.api.replacements.*;
033import com.oracle.graal.asm.*;
034import com.oracle.graal.compiler.common.spi.*;
035import com.oracle.graal.graph.Node.ConstantNodeParameter;
036import com.oracle.graal.graph.Node.NodeIntrinsic;
037import com.oracle.graal.hotspot.*;
038import com.oracle.graal.hotspot.meta.*;
039import com.oracle.graal.hotspot.nodes.*;
040import com.oracle.graal.nodes.*;
041import com.oracle.graal.replacements.*;
042import com.oracle.graal.replacements.Snippet.ConstantParameter;
043import com.oracle.graal.word.*;
044
045/**
046 * Uncommon trap stub.
047 *
048 * This is the entry point for code which is returning to a de-optimized frame.
049 *
050 * The steps taken by this frame are as follows:
051 *
052 * <li>push a dummy "register_save" and save the return values (O0, O1, F0/F1, G1) and all
053 * potentially live registers (at a pollpoint many registers can be live).
054 *
055 * <li>call the C routine: Deoptimization::fetch_unroll_info (this function returns information
056 * about the number and size of interpreter frames which are equivalent to the frame which is being
057 * deoptimized)
058 *
059 * <li>deallocate the unpack frame, restoring only results values. Other volatile registers will now
060 * be captured in the vframeArray as needed.
061 *
062 * <li>deallocate the deoptimization frame
063 *
064 * <li>in a loop using the information returned in the previous step push new interpreter frames
065 * (take care to propagate the return values through each new frame pushed)
066 *
067 * <li>create a dummy "unpack_frame" and save the return values (O0, O1, F0)
068 *
069 * <li>call the C routine: Deoptimization::unpack_frames (this function lays out values on the
070 * interpreter frame which was just created)
071 *
072 * <li>deallocate the dummy unpack_frame
073 *
074 * <li>ensure that all the return values are correctly set and then do a return to the interpreter
075 * entry point
076 *
077 * <p>
078 * <b>ATTENTION: We cannot do any complicated operations e.g. logging via printf in this snippet
079 * because we change the current stack layout and so the code is very sensitive to register
080 * allocation.</b>
081 */
082public class UncommonTrapStub extends SnippetStub {
083
084    public static final LocationIdentity STACK_BANG_LOCATION = NamedLocationIdentity.mutable("stack bang");
085
086    private final TargetDescription target;
087
088    public UncommonTrapStub(HotSpotProviders providers, TargetDescription target, HotSpotForeignCallLinkage linkage) {
089        super(UncommonTrapStub.class, "uncommonTrapHandler", providers, linkage);
090        this.target = target;
091        assert PreferGraalStubs.getValue();
092    }
093
094    @Override
095    public boolean preservesRegisters() {
096        return false;
097    }
098
099    @Override
100    protected Object getConstantParameterValue(int index, String name) {
101        switch (index) {
102            case 0:
103                return providers.getRegisters().getThreadRegister();
104            case 1:
105                return providers.getRegisters().getStackPointerRegister();
106            default:
107                throw JVMCIError.shouldNotReachHere("unknown parameter " + name + " at index " + index);
108        }
109    }
110
111    /**
112     * Uncommon trap handler.
113     *
114     * We save the argument return registers. We call the first C routine, fetch_unroll_info(). This
115     * routine captures the return values and returns a structure which describes the current frame
116     * size and the sizes of all replacement frames. The current frame is compiled code and may
117     * contain many inlined functions, each with their own JVM state. We pop the current frame, then
118     * push all the new frames. Then we call the C routine unpack_frames() to populate these frames.
119     * Finally unpack_frames() returns us the new target address. Notice that callee-save registers
120     * are BLOWN here; they have already been captured in the vframeArray at the time the return PC
121     * was patched.
122     */
123    @Snippet
124    private static void uncommonTrapHandler(@ConstantParameter Register threadRegister, @ConstantParameter Register stackPointerRegister) {
125        final Word thread = registerAsWord(threadRegister);
126        final long registerSaver = SaveAllRegistersNode.saveAllRegisters();
127
128        final int actionAndReason = readPendingDeoptimization(thread);
129        writePendingDeoptimization(thread, -1);
130
131        final Word unrollBlock = UncommonTrapCallNode.uncommonTrap(registerSaver, actionAndReason);
132
133        // Pop all the frames we must move/replace.
134        //
135        // Frame picture (youngest to oldest)
136        // 1: self-frame
137        // 2: deoptimizing frame
138        // 3: caller of deoptimizing frame (could be compiled/interpreted).
139
140        // Pop self-frame.
141        LeaveCurrentStackFrameNode.leaveCurrentStackFrame(registerSaver);
142
143        // Load the initial info we should save (e.g. frame pointer).
144        final Word initialInfo = unrollBlock.readWord(deoptimizationUnrollBlockInitialInfoOffset());
145
146        // Pop deoptimized frame.
147        final int sizeOfDeoptimizedFrame = unrollBlock.readInt(deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset());
148        LeaveDeoptimizedStackFrameNode.leaveDeoptimizedStackFrame(sizeOfDeoptimizedFrame, initialInfo);
149
150        /*
151         * Stack bang to make sure there's enough room for the interpreter frames. Bang stack for
152         * total size of the interpreter frames plus shadow page size. Bang one page at a time
153         * because large sizes can bang beyond yellow and red zones.
154         * 
155         * @deprecated This code should go away as soon as JDK-8032410 hits the Graal repository.
156         */
157        final int totalFrameSizes = unrollBlock.readInt(deoptimizationUnrollBlockTotalFrameSizesOffset());
158        final int bangPages = NumUtil.roundUp(totalFrameSizes, pageSize()) / pageSize() + stackShadowPages();
159        Word stackPointer = readRegister(stackPointerRegister);
160
161        for (int i = 1; i < bangPages; i++) {
162            stackPointer.writeInt((-i * pageSize()) + stackBias(), 0, STACK_BANG_LOCATION);
163        }
164
165        // Load number of interpreter frames.
166        final int numberOfFrames = unrollBlock.readInt(deoptimizationUnrollBlockNumberOfFramesOffset());
167
168        // Load address of array of frame sizes.
169        final Word frameSizes = unrollBlock.readWord(deoptimizationUnrollBlockFrameSizesOffset());
170
171        // Load address of array of frame PCs.
172        final Word framePcs = unrollBlock.readWord(deoptimizationUnrollBlockFramePcsOffset());
173
174        /*
175         * Get the current stack pointer (sender's original SP) before adjustment so that we can
176         * save it in the skeletal interpreter frame.
177         */
178        Word senderSp = readRegister(stackPointerRegister);
179
180        // Adjust old interpreter frame to make space for new frame's extra Java locals.
181        final int callerAdjustment = unrollBlock.readInt(deoptimizationUnrollBlockCallerAdjustmentOffset());
182        writeRegister(stackPointerRegister, readRegister(stackPointerRegister).subtract(callerAdjustment));
183
184        for (int i = 0; i < numberOfFrames; i++) {
185            final Word frameSize = frameSizes.readWord(i * wordSize());
186            final Word framePc = framePcs.readWord(i * wordSize());
187
188            // Push an interpreter frame onto the stack.
189            PushInterpreterFrameNode.pushInterpreterFrame(frameSize, framePc, senderSp, initialInfo);
190
191            // Get the current stack pointer (sender SP) and pass it to next frame.
192            senderSp = readRegister(stackPointerRegister);
193        }
194
195        // Get final return address.
196        final Word framePc = framePcs.readWord(numberOfFrames * wordSize());
197
198        /*
199         * Enter a frame to call out to unpack frames. Since we changed the stack pointer to an
200         * unknown alignment we need to align it here before calling C++ code.
201         */
202        final Word senderFp = initialInfo;
203        EnterUnpackFramesStackFrameNode.enterUnpackFramesStackFrame(framePc, senderSp, senderFp, registerSaver);
204
205        // Pass uncommon trap mode to unpack frames.
206        final int mode = deoptimizationUnpackUncommonTrap();
207        unpackFrames(UNPACK_FRAMES, thread, mode);
208
209        LeaveUnpackFramesStackFrameNode.leaveUnpackFramesStackFrame(registerSaver);
210    }
211
212    /**
213     * Reads the value of the passed register as a Word.
214     */
215    private static Word readRegister(Register register) {
216        return registerAsWord(register, false, false);
217    }
218
219    /**
220     * Writes the value of the passed register.
221     *
222     * @param value value the register should be set to
223     */
224    private static void writeRegister(Register register, Word value) {
225        writeRegisterAsWord(register, value);
226    }
227
228    @Fold
229    private static int stackShadowPages() {
230        return config().useStackBanging ? config().stackShadowPages : 0;
231    }
232
233    /**
234     * Returns the stack bias for the host architecture.
235     *
236     * @deprecated This method should go away as soon as JDK-8032410 hits the Graal repository.
237     *
238     * @return stack bias
239     */
240    @Deprecated
241    @Fold
242    private static int stackBias() {
243        return config().stackBias;
244    }
245
246    @Fold
247    private static int deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset() {
248        return config().deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset;
249    }
250
251    @Fold
252    private static int deoptimizationUnrollBlockCallerAdjustmentOffset() {
253        return config().deoptimizationUnrollBlockCallerAdjustmentOffset;
254    }
255
256    @Fold
257    private static int deoptimizationUnrollBlockNumberOfFramesOffset() {
258        return config().deoptimizationUnrollBlockNumberOfFramesOffset;
259    }
260
261    @Fold
262    private static int deoptimizationUnrollBlockTotalFrameSizesOffset() {
263        return config().deoptimizationUnrollBlockTotalFrameSizesOffset;
264    }
265
266    @Fold
267    private static int deoptimizationUnrollBlockFrameSizesOffset() {
268        return config().deoptimizationUnrollBlockFrameSizesOffset;
269    }
270
271    @Fold
272    private static int deoptimizationUnrollBlockFramePcsOffset() {
273        return config().deoptimizationUnrollBlockFramePcsOffset;
274    }
275
276    @Fold
277    private static int deoptimizationUnrollBlockInitialInfoOffset() {
278        return config().deoptimizationUnrollBlockInitialInfoOffset;
279    }
280
281    @Fold
282    private static int deoptimizationUnpackDeopt() {
283        return config().deoptimizationUnpackDeopt;
284    }
285
286    @Fold
287    private static int deoptimizationUnpackUncommonTrap() {
288        return config().deoptimizationUnpackUncommonTrap;
289    }
290
291    @NodeIntrinsic(value = StubForeignCallNode.class, setStampFromReturnType = true)
292    public static native int unpackFrames(@ConstantNodeParameter ForeignCallDescriptor unpackFrames, Word thread, int mode);
293}