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  */
  23 package org.graalvm.compiler.lir.framemap;
  24 
  25 import java.util.ArrayList;
  26 import java.util.BitSet;
  27 import java.util.List;
  28 
  29 import org.graalvm.compiler.core.common.NumUtil;
  30 import org.graalvm.compiler.core.common.PermanentBailoutException;
  31 import org.graalvm.compiler.core.common.LIRKind;
  32 
  33 import jdk.vm.ci.code.Architecture;
  34 import jdk.vm.ci.code.CallingConvention;
  35 import jdk.vm.ci.code.CodeCacheProvider;
  36 import jdk.vm.ci.code.RegisterConfig;
  37 import jdk.vm.ci.code.StackSlot;
  38 import jdk.vm.ci.code.TargetDescription;
  39 import jdk.vm.ci.meta.Value;
  40 import 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  */
  50 public 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 }