| /* |
| * Copyright (c) 2009, 2015, 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.lir.framemap; |
| |
| import java.util.ArrayList; |
| import java.util.BitSet; |
| import java.util.List; |
| |
| import org.graalvm.compiler.asm.NumUtil; |
| import org.graalvm.compiler.common.PermanentBailoutException; |
| import org.graalvm.compiler.core.common.LIRKind; |
| |
| import jdk.vm.ci.code.Architecture; |
| import jdk.vm.ci.code.CallingConvention; |
| import jdk.vm.ci.code.CodeCacheProvider; |
| import jdk.vm.ci.code.RegisterConfig; |
| import jdk.vm.ci.code.StackSlot; |
| import jdk.vm.ci.code.TargetDescription; |
| import jdk.vm.ci.meta.Value; |
| import jdk.vm.ci.meta.ValueKind; |
| |
| /** |
| * This class is used to build the stack frame layout for a compiled method. A {@link StackSlot} is |
| * used to index slots of the frame relative to the stack pointer. The frame size is only fixed |
| * after register allocation when all spill slots have been allocated. Both the outgoing argument |
| * area and the spill are can grow until then. Therefore, outgoing arguments are indexed from the |
| * stack pointer, while spill slots are indexed from the beginning of the frame (and the total frame |
| * size has to be added to get the actual offset from the stack pointer). |
| */ |
| public abstract class FrameMap { |
| |
| private final TargetDescription target; |
| private final RegisterConfig registerConfig; |
| |
| public interface ReferenceMapBuilderFactory { |
| |
| ReferenceMapBuilder newReferenceMapBuilder(int totalFrameSize); |
| } |
| |
| private final ReferenceMapBuilderFactory referenceMapFactory; |
| |
| /** |
| * The final frame size, not including the size of the |
| * {@link Architecture#getReturnAddressSize() return address slot}. The value is only set after |
| * register allocation is complete, i.e., after all spill slots have been allocated. |
| */ |
| private int frameSize; |
| |
| /** |
| * Initial size of the area occupied by spill slots and other stack-allocated memory blocks. |
| */ |
| protected int initialSpillSize; |
| |
| /** |
| * Size of the area occupied by spill slots and other stack-allocated memory blocks. |
| */ |
| protected int spillSize; |
| |
| /** |
| * Size of the area occupied by outgoing overflow arguments. This value is adjusted as calling |
| * conventions for outgoing calls are retrieved. On some platforms, there is a minimum outgoing |
| * size even if no overflow arguments are on the stack. |
| */ |
| protected int outgoingSize; |
| |
| /** |
| * Determines if this frame has values on the stack for outgoing calls. |
| */ |
| protected boolean hasOutgoingStackArguments; |
| |
| /** |
| * The list of stack slots allocated in this frame that are present in every reference map. |
| */ |
| private final List<StackSlot> objectStackSlots; |
| |
| /** |
| * Records whether an offset to an incoming stack argument was ever returned by |
| * {@link #offsetForStackSlot(StackSlot)}. |
| */ |
| private boolean accessesCallerFrame; |
| |
| /** |
| * Creates a new frame map for the specified method. The given registerConfig is optional, in |
| * case null is passed the default RegisterConfig from the CodeCacheProvider will be used. |
| */ |
| public FrameMap(CodeCacheProvider codeCache, RegisterConfig registerConfig, ReferenceMapBuilderFactory referenceMapFactory) { |
| this.target = codeCache.getTarget(); |
| this.registerConfig = registerConfig == null ? codeCache.getRegisterConfig() : registerConfig; |
| this.frameSize = -1; |
| this.outgoingSize = codeCache.getMinimumOutgoingSize(); |
| this.objectStackSlots = new ArrayList<>(); |
| this.referenceMapFactory = referenceMapFactory; |
| } |
| |
| public RegisterConfig getRegisterConfig() { |
| return registerConfig; |
| } |
| |
| public TargetDescription getTarget() { |
| return target; |
| } |
| |
| public void addLiveValues(ReferenceMapBuilder refMap) { |
| for (Value value : objectStackSlots) { |
| refMap.addLiveValue(value); |
| } |
| } |
| |
| protected int returnAddressSize() { |
| return getTarget().arch.getReturnAddressSize(); |
| } |
| |
| /** |
| * Determines if an offset to an incoming stack argument was ever returned by |
| * {@link #offsetForStackSlot(StackSlot)}. |
| */ |
| public boolean accessesCallerFrame() { |
| return accessesCallerFrame; |
| } |
| |
| /** |
| * Gets the frame size of the compiled frame, not including the size of the |
| * {@link Architecture#getReturnAddressSize() return address slot}. |
| * |
| * @return The size of the frame (in bytes). |
| */ |
| public int frameSize() { |
| assert frameSize != -1 : "frame size not computed yet"; |
| return frameSize; |
| } |
| |
| public int outgoingSize() { |
| return outgoingSize; |
| } |
| |
| /** |
| * Determines if any space is used in the frame apart from the |
| * {@link Architecture#getReturnAddressSize() return address slot}. |
| */ |
| public boolean frameNeedsAllocating() { |
| int unalignedFrameSize = spillSize - returnAddressSize(); |
| return hasOutgoingStackArguments || unalignedFrameSize != 0; |
| } |
| |
| /** |
| * Gets the total frame size of the compiled frame, including the size of the |
| * {@link Architecture#getReturnAddressSize() return address slot}. |
| * |
| * @return The total size of the frame (in bytes). |
| */ |
| public abstract int totalFrameSize(); |
| |
| /** |
| * Gets the current size of this frame. This is the size that would be returned by |
| * {@link #frameSize()} if {@link #finish()} were called now. |
| */ |
| public abstract int currentFrameSize(); |
| |
| /** |
| * Aligns the given frame size to the stack alignment size and return the aligned size. |
| * |
| * @param size the initial frame size to be aligned |
| * @return the aligned frame size |
| */ |
| protected int alignFrameSize(int size) { |
| return NumUtil.roundUp(size, getTarget().stackAlignment); |
| } |
| |
| /** |
| * Computes the final size of this frame. After this method has been called, methods that change |
| * the frame size cannot be called anymore, e.g., no more spill slots or outgoing arguments can |
| * be requested. |
| */ |
| public void finish() { |
| frameSize = currentFrameSize(); |
| if (frameSize > getRegisterConfig().getMaximumFrameSize()) { |
| throw new PermanentBailoutException("Frame size (%d) exceeded maximum allowed frame size (%d).", frameSize, getRegisterConfig().getMaximumFrameSize()); |
| } |
| } |
| |
| /** |
| * Computes the offset of a stack slot relative to the frame register. |
| * |
| * @param slot a stack slot |
| * @return the offset of the stack slot |
| */ |
| public int offsetForStackSlot(StackSlot slot) { |
| if (slot.isInCallerFrame()) { |
| accessesCallerFrame = true; |
| } |
| return slot.getOffset(totalFrameSize()); |
| } |
| |
| /** |
| * Informs the frame map that the compiled code calls a particular method, which may need stack |
| * space for outgoing arguments. |
| * |
| * @param cc The calling convention for the called method. |
| */ |
| public void callsMethod(CallingConvention cc) { |
| reserveOutgoing(cc.getStackSize()); |
| } |
| |
| /** |
| * Reserves space for stack-based outgoing arguments. |
| * |
| * @param argsSize The amount of space (in bytes) to reserve for stack-based outgoing arguments. |
| */ |
| public void reserveOutgoing(int argsSize) { |
| assert frameSize == -1 : "frame size must not yet be fixed"; |
| outgoingSize = Math.max(outgoingSize, argsSize); |
| hasOutgoingStackArguments = hasOutgoingStackArguments || argsSize > 0; |
| } |
| |
| /** |
| * Reserves a new spill slot in the frame of the method being compiled. The returned slot is |
| * aligned on its natural alignment, i.e., an 8-byte spill slot is aligned at an 8-byte |
| * boundary. |
| * |
| * @param kind The kind of the spill slot to be reserved. |
| * @param additionalOffset |
| * @return A spill slot denoting the reserved memory area. |
| */ |
| protected StackSlot allocateNewSpillSlot(ValueKind<?> kind, int additionalOffset) { |
| return StackSlot.get(kind, -spillSize + additionalOffset, true); |
| } |
| |
| /** |
| * Returns the spill slot size for the given {@link ValueKind}. The default value is the size in |
| * bytes for the target architecture. |
| * |
| * @param kind the {@link ValueKind} to be stored in the spill slot. |
| * @return the size in bytes |
| */ |
| public int spillSlotSize(ValueKind<?> kind) { |
| return kind.getPlatformKind().getSizeInBytes(); |
| } |
| |
| /** |
| * Reserves a spill slot in the frame of the method being compiled. The returned slot is aligned |
| * on its natural alignment, i.e., an 8-byte spill slot is aligned at an 8-byte boundary, unless |
| * overridden by a subclass. |
| * |
| * @param kind The kind of the spill slot to be reserved. |
| * @return A spill slot denoting the reserved memory area. |
| */ |
| public StackSlot allocateSpillSlot(ValueKind<?> kind) { |
| assert frameSize == -1 : "frame size must not yet be fixed"; |
| int size = spillSlotSize(kind); |
| spillSize = NumUtil.roundUp(spillSize + size, size); |
| return allocateNewSpillSlot(kind, 0); |
| } |
| |
| /** |
| * Returns the size of the stack slot range for {@code slots} objects. |
| * |
| * @param slots The number of slots. |
| * @return The size in byte |
| */ |
| public int spillSlotRangeSize(int slots) { |
| return slots * getTarget().wordSize; |
| } |
| |
| /** |
| * Reserves a number of contiguous slots in the frame of the method being compiled. If the |
| * requested number of slots is 0, this method returns {@code null}. |
| * |
| * @param slots the number of slots to reserve |
| * @param objects specifies the indexes of the object pointer slots. The caller is responsible |
| * for guaranteeing that each such object pointer slot is initialized before any |
| * instruction that uses a reference map. Without this guarantee, the garbage |
| * collector could see garbage object values. |
| * @return the first reserved stack slot (i.e., at the lowest address) |
| */ |
| public StackSlot allocateStackSlots(int slots, BitSet objects) { |
| assert frameSize == -1 : "frame size must not yet be fixed"; |
| if (slots == 0) { |
| return null; |
| } |
| spillSize += spillSlotRangeSize(slots); |
| |
| if (!objects.isEmpty()) { |
| assert objects.length() <= slots; |
| StackSlot result = null; |
| for (int slotIndex = 0; slotIndex < slots; slotIndex++) { |
| StackSlot objectSlot = null; |
| if (objects.get(slotIndex)) { |
| objectSlot = allocateNewSpillSlot(LIRKind.reference(getTarget().arch.getWordKind()), slotIndex * getTarget().wordSize); |
| addObjectStackSlot(objectSlot); |
| } |
| if (slotIndex == 0) { |
| if (objectSlot != null) { |
| result = objectSlot; |
| } else { |
| result = allocateNewSpillSlot(LIRKind.value(getTarget().arch.getWordKind()), 0); |
| } |
| } |
| } |
| assert result != null; |
| return result; |
| |
| } else { |
| return allocateNewSpillSlot(LIRKind.value(getTarget().arch.getWordKind()), 0); |
| } |
| } |
| |
| protected void addObjectStackSlot(StackSlot objectSlot) { |
| objectStackSlots.add(objectSlot); |
| } |
| |
| public ReferenceMapBuilder newReferenceMapBuilder() { |
| return referenceMapFactory.newReferenceMapBuilder(totalFrameSize()); |
| } |
| } |