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 }