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