/*
 * 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.core.common.NumUtil;
import org.graalvm.compiler.core.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 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).
/** * 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 return address slot. The value is only set after register allocation is complete, i.e., after all spill slots have been allocated.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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 offsetForStackSlot(StackSlot).
/** * 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.
/** * 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 offsetForStackSlot(StackSlot).
/** * 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 return address slot.
Returns:The size of the frame (in bytes).
/** * 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 return address slot.
/** * 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 return address slot.
Returns:The total size of the frame (in bytes).
/** * 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 frameSize() if finish() were called now.
/** * 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.
Params:
  • size – the initial frame size to be aligned
Returns:the aligned frame size
/** * 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.
/** * 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.
Params:
  • slot – a stack slot
Returns:the offset of the stack slot
/** * 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.
Params:
  • cc – The calling convention for the called method.
/** * 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.
Params:
  • argsSize – The amount of space (in bytes) to reserve for stack-based outgoing arguments.
/** * 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.
Params:
  • kind – The kind of the spill slot to be reserved.
  • additionalOffset –
Returns:A spill slot denoting the reserved memory area.
/** * 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 ValueKind. The default value is the size in bytes for the target architecture.
Params:
  • kind – the ValueKind to be stored in the spill slot.
Returns:the size in bytes
/** * 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.
Params:
  • kind – The kind of the spill slot to be reserved.
Returns:A spill slot denoting the reserved memory area.
/** * 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 slots objects.
Params:
  • slots – The number of slots.
Returns:The size in byte
/** * 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 null.
Params:
  • slots – the number of slots to reserve
  • 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.
Returns:the first reserved stack slot (i.e., at the lowest address)
/** * 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()); } }