FrameMap.java revision 12651:6ef01bd40ce2
1/*
2 * Copyright (c) 2009, 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 */
23package org.graalvm.compiler.lir.framemap;
24
25import java.util.ArrayList;
26import java.util.BitSet;
27import java.util.List;
28
29import org.graalvm.compiler.asm.NumUtil;
30import org.graalvm.compiler.common.PermanentBailoutException;
31import org.graalvm.compiler.core.common.LIRKind;
32
33import jdk.vm.ci.code.Architecture;
34import jdk.vm.ci.code.CallingConvention;
35import jdk.vm.ci.code.CodeCacheProvider;
36import jdk.vm.ci.code.RegisterConfig;
37import jdk.vm.ci.code.StackSlot;
38import jdk.vm.ci.code.TargetDescription;
39import jdk.vm.ci.meta.Value;
40import jdk.vm.ci.meta.ValueKind;
41
42/**
43 * This class is used to build the stack frame layout for a compiled method. A {@link StackSlot} is
44 * used to index slots of the frame relative to the stack pointer. The frame size is only fixed
45 * after register allocation when all spill slots have been allocated. Both the outgoing argument
46 * area and the spill are can grow until then. Therefore, outgoing arguments are indexed from the
47 * stack pointer, while spill slots are indexed from the beginning of the frame (and the total frame
48 * size has to be added to get the actual offset from the stack pointer).
49 */
50public abstract class FrameMap {
51
52    private final TargetDescription target;
53    private final RegisterConfig registerConfig;
54
55    public interface ReferenceMapBuilderFactory {
56
57        ReferenceMapBuilder newReferenceMapBuilder(int totalFrameSize);
58    }
59
60    private final ReferenceMapBuilderFactory referenceMapFactory;
61
62    /**
63     * The final frame size, not including the size of the
64     * {@link Architecture#getReturnAddressSize() return address slot}. The value is only set after
65     * register allocation is complete, i.e., after all spill slots have been allocated.
66     */
67    private int frameSize;
68
69    /**
70     * Initial size of the area occupied by spill slots and other stack-allocated memory blocks.
71     */
72    protected int initialSpillSize;
73
74    /**
75     * Size of the area occupied by spill slots and other stack-allocated memory blocks.
76     */
77    protected int spillSize;
78
79    /**
80     * Size of the area occupied by outgoing overflow arguments. This value is adjusted as calling
81     * conventions for outgoing calls are retrieved. On some platforms, there is a minimum outgoing
82     * size even if no overflow arguments are on the stack.
83     */
84    protected int outgoingSize;
85
86    /**
87     * Determines if this frame has values on the stack for outgoing calls.
88     */
89    protected boolean hasOutgoingStackArguments;
90
91    /**
92     * The list of stack slots allocated in this frame that are present in every reference map.
93     */
94    private final List<StackSlot> objectStackSlots;
95
96    /**
97     * Records whether an offset to an incoming stack argument was ever returned by
98     * {@link #offsetForStackSlot(StackSlot)}.
99     */
100    private boolean accessesCallerFrame;
101
102    /**
103     * Creates a new frame map for the specified method. The given registerConfig is optional, in
104     * case null is passed the default RegisterConfig from the CodeCacheProvider will be used.
105     */
106    public FrameMap(CodeCacheProvider codeCache, RegisterConfig registerConfig, ReferenceMapBuilderFactory referenceMapFactory) {
107        this.target = codeCache.getTarget();
108        this.registerConfig = registerConfig == null ? codeCache.getRegisterConfig() : registerConfig;
109        this.frameSize = -1;
110        this.outgoingSize = codeCache.getMinimumOutgoingSize();
111        this.objectStackSlots = new ArrayList<>();
112        this.referenceMapFactory = referenceMapFactory;
113    }
114
115    public RegisterConfig getRegisterConfig() {
116        return registerConfig;
117    }
118
119    public TargetDescription getTarget() {
120        return target;
121    }
122
123    public void addLiveValues(ReferenceMapBuilder refMap) {
124        for (Value value : objectStackSlots) {
125            refMap.addLiveValue(value);
126        }
127    }
128
129    protected int returnAddressSize() {
130        return getTarget().arch.getReturnAddressSize();
131    }
132
133    /**
134     * Determines if an offset to an incoming stack argument was ever returned by
135     * {@link #offsetForStackSlot(StackSlot)}.
136     */
137    public boolean accessesCallerFrame() {
138        return accessesCallerFrame;
139    }
140
141    /**
142     * Gets the frame size of the compiled frame, not including the size of the
143     * {@link Architecture#getReturnAddressSize() return address slot}.
144     *
145     * @return The size of the frame (in bytes).
146     */
147    public int frameSize() {
148        assert frameSize != -1 : "frame size not computed yet";
149        return frameSize;
150    }
151
152    public int outgoingSize() {
153        return outgoingSize;
154    }
155
156    /**
157     * Determines if any space is used in the frame apart from the
158     * {@link Architecture#getReturnAddressSize() return address slot}.
159     */
160    public boolean frameNeedsAllocating() {
161        int unalignedFrameSize = spillSize - returnAddressSize();
162        return hasOutgoingStackArguments || unalignedFrameSize != 0;
163    }
164
165    /**
166     * Gets the total frame size of the compiled frame, including the size of the
167     * {@link Architecture#getReturnAddressSize() return address slot}.
168     *
169     * @return The total size of the frame (in bytes).
170     */
171    public abstract int totalFrameSize();
172
173    /**
174     * Gets the current size of this frame. This is the size that would be returned by
175     * {@link #frameSize()} if {@link #finish()} were called now.
176     */
177    public abstract int currentFrameSize();
178
179    /**
180     * Aligns the given frame size to the stack alignment size and return the aligned size.
181     *
182     * @param size the initial frame size to be aligned
183     * @return the aligned frame size
184     */
185    protected int alignFrameSize(int size) {
186        return NumUtil.roundUp(size, getTarget().stackAlignment);
187    }
188
189    /**
190     * Computes the final size of this frame. After this method has been called, methods that change
191     * the frame size cannot be called anymore, e.g., no more spill slots or outgoing arguments can
192     * be requested.
193     */
194    public void finish() {
195        frameSize = currentFrameSize();
196        if (frameSize > getRegisterConfig().getMaximumFrameSize()) {
197            throw new PermanentBailoutException("Frame size (%d) exceeded maximum allowed frame size (%d).", frameSize, getRegisterConfig().getMaximumFrameSize());
198        }
199    }
200
201    /**
202     * Computes the offset of a stack slot relative to the frame register.
203     *
204     * @param slot a stack slot
205     * @return the offset of the stack slot
206     */
207    public int offsetForStackSlot(StackSlot slot) {
208        if (slot.isInCallerFrame()) {
209            accessesCallerFrame = true;
210        }
211        return slot.getOffset(totalFrameSize());
212    }
213
214    /**
215     * Informs the frame map that the compiled code calls a particular method, which may need stack
216     * space for outgoing arguments.
217     *
218     * @param cc The calling convention for the called method.
219     */
220    public void callsMethod(CallingConvention cc) {
221        reserveOutgoing(cc.getStackSize());
222    }
223
224    /**
225     * Reserves space for stack-based outgoing arguments.
226     *
227     * @param argsSize The amount of space (in bytes) to reserve for stack-based outgoing arguments.
228     */
229    public void reserveOutgoing(int argsSize) {
230        assert frameSize == -1 : "frame size must not yet be fixed";
231        outgoingSize = Math.max(outgoingSize, argsSize);
232        hasOutgoingStackArguments = hasOutgoingStackArguments || argsSize > 0;
233    }
234
235    /**
236     * Reserves a new spill slot in the frame of the method being compiled. The returned slot is
237     * aligned on its natural alignment, i.e., an 8-byte spill slot is aligned at an 8-byte
238     * boundary.
239     *
240     * @param kind The kind of the spill slot to be reserved.
241     * @param additionalOffset
242     * @return A spill slot denoting the reserved memory area.
243     */
244    protected StackSlot allocateNewSpillSlot(ValueKind<?> kind, int additionalOffset) {
245        return StackSlot.get(kind, -spillSize + additionalOffset, true);
246    }
247
248    /**
249     * Returns the spill slot size for the given {@link ValueKind}. The default value is the size in
250     * bytes for the target architecture.
251     *
252     * @param kind the {@link ValueKind} to be stored in the spill slot.
253     * @return the size in bytes
254     */
255    public int spillSlotSize(ValueKind<?> kind) {
256        return kind.getPlatformKind().getSizeInBytes();
257    }
258
259    /**
260     * Reserves a spill slot in the frame of the method being compiled. The returned slot is aligned
261     * on its natural alignment, i.e., an 8-byte spill slot is aligned at an 8-byte boundary, unless
262     * overridden by a subclass.
263     *
264     * @param kind The kind of the spill slot to be reserved.
265     * @return A spill slot denoting the reserved memory area.
266     */
267    public StackSlot allocateSpillSlot(ValueKind<?> kind) {
268        assert frameSize == -1 : "frame size must not yet be fixed";
269        int size = spillSlotSize(kind);
270        spillSize = NumUtil.roundUp(spillSize + size, size);
271        return allocateNewSpillSlot(kind, 0);
272    }
273
274    /**
275     * Returns the size of the stack slot range for {@code slots} objects.
276     *
277     * @param slots The number of slots.
278     * @return The size in byte
279     */
280    public int spillSlotRangeSize(int slots) {
281        return slots * getTarget().wordSize;
282    }
283
284    /**
285     * Reserves a number of contiguous slots in the frame of the method being compiled. If the
286     * requested number of slots is 0, this method returns {@code null}.
287     *
288     * @param slots the number of slots to reserve
289     * @param objects specifies the indexes of the object pointer slots. The caller is responsible
290     *            for guaranteeing that each such object pointer slot is initialized before any
291     *            instruction that uses a reference map. Without this guarantee, the garbage
292     *            collector could see garbage object values.
293     * @return the first reserved stack slot (i.e., at the lowest address)
294     */
295    public StackSlot allocateStackSlots(int slots, BitSet objects) {
296        assert frameSize == -1 : "frame size must not yet be fixed";
297        if (slots == 0) {
298            return null;
299        }
300        spillSize += spillSlotRangeSize(slots);
301
302        if (!objects.isEmpty()) {
303            assert objects.length() <= slots;
304            StackSlot result = null;
305            for (int slotIndex = 0; slotIndex < slots; slotIndex++) {
306                StackSlot objectSlot = null;
307                if (objects.get(slotIndex)) {
308                    objectSlot = allocateNewSpillSlot(LIRKind.reference(getTarget().arch.getWordKind()), slotIndex * getTarget().wordSize);
309                    addObjectStackSlot(objectSlot);
310                }
311                if (slotIndex == 0) {
312                    if (objectSlot != null) {
313                        result = objectSlot;
314                    } else {
315                        result = allocateNewSpillSlot(LIRKind.value(getTarget().arch.getWordKind()), 0);
316                    }
317                }
318            }
319            assert result != null;
320            return result;
321
322        } else {
323            return allocateNewSpillSlot(LIRKind.value(getTarget().arch.getWordKind()), 0);
324        }
325    }
326
327    protected void addObjectStackSlot(StackSlot objectSlot) {
328        objectStackSlots.add(objectSlot);
329    }
330
331    public ReferenceMapBuilder newReferenceMapBuilder() {
332        return referenceMapFactory.newReferenceMapBuilder(totalFrameSize());
333    }
334}
335