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 }