Label.java revision 953:221a84ef44c0
1/* 2 * Copyright (c) 2010, 2013, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25package jdk.nashorn.internal.codegen; 26 27import java.util.ArrayList; 28import java.util.Arrays; 29import java.util.BitSet; 30import java.util.Iterator; 31import java.util.List; 32import java.util.ListIterator; 33import jdk.nashorn.internal.codegen.types.Type; 34 35/** 36 * Abstraction for labels, separating a label from the underlying 37 * byte code emitter. Also augmenting label with e.g. a name 38 * for easier debugging and reading code 39 * 40 * see -Dnashorn.codegen.debug, --log=codegen 41 */ 42public final class Label { 43 //byte code generation evaluation type stack for consistency check 44 //and correct opcode selection. one per label as a label may be a 45 //join point 46 static final class Stack implements Cloneable { 47 static final int NON_LOAD = -1; 48 49 Type[] data; 50 int[] localLoads; 51 int sp; 52 53 List<Type> localVariableTypes; 54 int firstTemp; // index of the first temporary local variable 55 // Bitmap marking last slot belonging to a single symbol. 56 BitSet symbolBoundary; 57 58 Stack() { 59 data = new Type[8]; 60 localLoads = new int[8]; 61 localVariableTypes = new ArrayList<>(8); 62 symbolBoundary = new BitSet(); 63 } 64 65 boolean isEmpty() { 66 return sp == 0; 67 } 68 69 int size() { 70 return sp; 71 } 72 73 void clear() { 74 sp = 0; 75 } 76 77 void push(final Type type) { 78 if (data.length == sp) { 79 final Type[] newData = new Type[sp * 2]; 80 final int[] newLocalLoad = new int[sp * 2]; 81 System.arraycopy(data, 0, newData, 0, sp); 82 System.arraycopy(localLoads, 0, newLocalLoad, 0, sp); 83 data = newData; 84 localLoads = newLocalLoad; 85 } 86 data[sp] = type; 87 localLoads[sp] = NON_LOAD; 88 sp++; 89 } 90 91 Type peek() { 92 return peek(0); 93 } 94 95 Type peek(final int n) { 96 final int pos = sp - 1 - n; 97 return pos < 0 ? null : data[pos]; 98 } 99 100 /** 101 * Retrieve the top <tt>count</tt> types on the stack without modifying it. 102 * 103 * @param count number of types to return 104 * @return array of Types 105 */ 106 Type[] getTopTypes(final int count) { 107 final Type[] topTypes = new Type[count]; 108 System.arraycopy(data, sp - count, topTypes, 0, count); 109 return topTypes; 110 } 111 112 int[] getLocalLoads(final int from, final int to) { 113 final int count = to - from; 114 final int[] topLocalLoads = new int[count]; 115 System.arraycopy(localLoads, from, topLocalLoads, 0, count); 116 return topLocalLoads; 117 } 118 119 /** 120 * Returns the number of used local variable slots, including all live stack-store temporaries. 121 * @return the number of used local variable slots, including all live stack-store temporaries. 122 */ 123 int getUsedSlotsWithLiveTemporaries() { 124 // There are at least as many as are declared by the current blocks. 125 int usedSlots = firstTemp; 126 // Look at every load on the stack, and bump the number of used slots up by the temporaries seen there. 127 for(int i = sp; i-->0;) { 128 final int slot = localLoads[i]; 129 if(slot != Label.Stack.NON_LOAD) { 130 final int afterSlot = slot + localVariableTypes.get(slot).getSlots(); 131 if(afterSlot > usedSlots) { 132 usedSlots = afterSlot; 133 } 134 } 135 } 136 return usedSlots; 137 } 138 139 /** 140 * 141 * @param joinOrigin the stack from the other branch. 142 */ 143 void joinFrom(final Stack joinOrigin, final boolean breakTarget) { 144 assert isStackCompatible(joinOrigin); 145 if(breakTarget) { 146 // As we're joining labels that can jump across block boundaries, the number of local variables can 147 // differ, and we should always respect the one having less variables. 148 firstTemp = Math.min(firstTemp, joinOrigin.firstTemp); 149 } else { 150 assert firstTemp == joinOrigin.firstTemp; 151 } 152 final int[] otherLoads = joinOrigin.localLoads; 153 int firstDeadTemp = firstTemp; 154 for(int i = 0; i < sp; ++i) { 155 final int localLoad = localLoads[i]; 156 if(localLoad != otherLoads[i]) { 157 localLoads[i] = NON_LOAD; 158 } else if(localLoad >= firstDeadTemp) { 159 firstDeadTemp = localLoad + localVariableTypes.get(localLoad).getSlots(); 160 } 161 } 162 // Eliminate dead temporaries 163 undefineLocalVariables(firstDeadTemp, false); 164 assert isVariablePartitioningEqual(joinOrigin, firstDeadTemp); 165 mergeVariableTypes(joinOrigin, firstDeadTemp); 166 } 167 168 private void mergeVariableTypes(final Stack joinOrigin, final int toSlot) { 169 final ListIterator<Type> it1 = localVariableTypes.listIterator(); 170 final Iterator<Type> it2 = joinOrigin.localVariableTypes.iterator(); 171 172 for(int i = 0; i < toSlot; ++i) { 173 final Type thisType = it1.next(); 174 final Type otherType = it2.next(); 175 if(otherType == Type.UNKNOWN) { 176 // Variables that are <unknown> on the other branch will become <unknown> here too. 177 it1.set(Type.UNKNOWN); 178 } else if (thisType != otherType) { 179 if(thisType.isObject() && otherType.isObject()) { 180 // different object types are merged into Object. 181 // TODO: maybe find most common superclass? 182 it1.set(Type.OBJECT); 183 } else { 184 assert thisType == Type.UNKNOWN; 185 } 186 } 187 } 188 } 189 190 void joinFromTry(final Stack joinOrigin) { 191 // As we're joining labels that can jump across block boundaries, the number of local variables can 192 // differ, and we should always respect the one having less variables. 193 firstTemp = Math.min(firstTemp, joinOrigin.firstTemp); 194 assert isVariablePartitioningEqual(joinOrigin, firstTemp); 195 mergeVariableTypes(joinOrigin, firstTemp); 196 } 197 198 private int getFirstDeadLocal(final List<Type> types) { 199 int i = types.size(); 200 for(final ListIterator<Type> it = types.listIterator(i); 201 it.hasPrevious() && it.previous() == Type.UNKNOWN; 202 --i) { 203 // no body 204 } 205 206 // Respect symbol boundaries; we never chop off half a symbol's storage 207 while(!symbolBoundary.get(i - 1)) { 208 ++i; 209 } 210 return i; 211 } 212 213 private boolean isStackCompatible(final Stack other) { 214 if (sp != other.sp) { 215 return false; 216 } 217 for (int i = 0; i < sp; i++) { 218 if (!data[i].isEquivalentTo(other.data[i])) { 219 return false; 220 } 221 } 222 return true; 223 } 224 225 private boolean isVariablePartitioningEqual(final Stack other, final int toSlot) { 226 // No difference in the symbol boundaries before the toSlot 227 final BitSet diff = other.getSymbolBoundaryCopy(); 228 diff.xor(symbolBoundary); 229 return diff.previousSetBit(toSlot - 1) == -1; 230 } 231 232 void markDeadLocalVariables(final int fromSlot, final int slotCount) { 233 final int localCount = localVariableTypes.size(); 234 if(fromSlot >= localCount) { 235 return; 236 } 237 final int toSlot = Math.min(fromSlot + slotCount, localCount); 238 invalidateLocalLoadsOnStack(fromSlot, toSlot); 239 for(int i = fromSlot; i < toSlot; ++i) { 240 localVariableTypes.set(i, Type.UNKNOWN); 241 } 242 } 243 244 @SuppressWarnings("unchecked") 245 List<Type> getLocalVariableTypesCopy() { 246 return (List<Type>)((ArrayList<Type>)localVariableTypes).clone(); 247 } 248 249 BitSet getSymbolBoundaryCopy() { 250 return (BitSet)symbolBoundary.clone(); 251 } 252 253 /** 254 * Returns a list of local variable slot types, but for those symbols that have multiple values, only the slot 255 * holding the widest type is marked as live. 256 * @return a list of widest local variable slot types. 257 */ 258 List<Type> getWidestLiveLocals(final List<Type> lvarTypes) { 259 final List<Type> widestLiveLocals = new ArrayList<>(lvarTypes); 260 boolean keepNextValue = true; 261 final int size = widestLiveLocals.size(); 262 for(int i = size - 1; i-- > 0;) { 263 if(symbolBoundary.get(i)) { 264 keepNextValue = true; 265 } 266 final Type t = widestLiveLocals.get(i); 267 if(t != Type.UNKNOWN) { 268 if(keepNextValue) { 269 if(t != Type.SLOT_2) { 270 keepNextValue = false; 271 } 272 } else { 273 widestLiveLocals.set(i, Type.UNKNOWN); 274 } 275 } 276 } 277 widestLiveLocals.subList(Math.max(getFirstDeadLocal(widestLiveLocals), firstTemp), widestLiveLocals.size()).clear(); 278 return widestLiveLocals; 279 } 280 281 String markSymbolBoundariesInLvarTypesDescriptor(final String lvarDescriptor) { 282 final char[] chars = lvarDescriptor.toCharArray(); 283 int j = 0; 284 for(int i = 0; i < chars.length; ++i) { 285 final char c = chars[i]; 286 final int nextj = j + CodeGeneratorLexicalContext.getTypeForSlotDescriptor(c).getSlots(); 287 if(!symbolBoundary.get(nextj - 1)) { 288 chars[i] = Character.toLowerCase(c); 289 } 290 j = nextj; 291 } 292 return new String(chars); 293 } 294 295 Type pop() { 296 assert sp > 0; 297 return data[--sp]; 298 } 299 300 @Override 301 public Stack clone() { 302 try { 303 final Stack clone = (Stack)super.clone(); 304 clone.data = data.clone(); 305 clone.localLoads = localLoads.clone(); 306 clone.symbolBoundary = getSymbolBoundaryCopy(); 307 clone.localVariableTypes = getLocalVariableTypesCopy(); 308 return clone; 309 } catch(final CloneNotSupportedException e) { 310 throw new AssertionError("", e); 311 } 312 } 313 314 private Stack cloneWithEmptyStack() { 315 final Stack stack = clone(); 316 stack.sp = 0; 317 return stack; 318 } 319 320 int getTopLocalLoad() { 321 return localLoads[sp - 1]; 322 } 323 324 void markLocalLoad(final int slot) { 325 localLoads[sp - 1] = slot; 326 } 327 328 /** 329 * Performs various bookeeping when a value is stored in a local variable slot. 330 * @param slot the slot written to 331 * @param onlySymbolLiveValue if true, this is the symbol's only live value, and other values of the symbol 332 * should be marked dead 333 * @param Type the type written to the slot 334 */ 335 void onLocalStore(final Type type, final int slot, final boolean onlySymbolLiveValue) { 336 if(onlySymbolLiveValue) { 337 final int fromSlot = slot == 0 ? 0 : (symbolBoundary.previousSetBit(slot - 1) + 1); 338 final int toSlot = symbolBoundary.nextSetBit(slot) + 1; 339 for(int i = fromSlot; i < toSlot; ++i) { 340 localVariableTypes.set(i, Type.UNKNOWN); 341 } 342 invalidateLocalLoadsOnStack(fromSlot, toSlot); 343 } else { 344 invalidateLocalLoadsOnStack(slot, slot + type.getSlots()); 345 } 346 347 localVariableTypes.set(slot, type); 348 if(type.isCategory2()) { 349 localVariableTypes.set(slot + 1, Type.SLOT_2); 350 } 351 } 352 353 /** 354 * Given a slot range, invalidate knowledge about local loads on stack from these slots (because they're being 355 * killed). 356 * @param fromSlot first slot, inclusive. 357 * @param toSlot last slot, exclusive. 358 */ 359 private void invalidateLocalLoadsOnStack(final int fromSlot, final int toSlot) { 360 for(int i = 0; i < sp; ++i) { 361 final int localLoad = localLoads[i]; 362 if(localLoad >= fromSlot && localLoad < toSlot) { 363 localLoads[i] = NON_LOAD; 364 } 365 } 366 } 367 368 /** 369 * Marks a range of slots as belonging to a defined local variable. The slots will start out with no live value 370 * in them. 371 * @param fromSlot first slot, inclusive. 372 * @param toSlot last slot, exclusive. 373 */ 374 void defineBlockLocalVariable(final int fromSlot, final int toSlot) { 375 defineLocalVariable(fromSlot, toSlot); 376 assert firstTemp < toSlot; 377 firstTemp = toSlot; 378 } 379 380 /** 381 * Defines a new temporary local variable and returns its allocated index. 382 * @param width the required width (in slots) for the new variable. 383 * @return the bytecode slot index where the newly allocated local begins. 384 */ 385 int defineTemporaryLocalVariable(final int width) { 386 final int fromSlot = getUsedSlotsWithLiveTemporaries(); 387 defineLocalVariable(fromSlot, fromSlot + width); 388 return fromSlot; 389 } 390 391 /** 392 * Marks a range of slots as belonging to a defined temporary local variable. The slots will start out with no 393 * live value in them. 394 * @param fromSlot first slot, inclusive. 395 * @param toSlot last slot, exclusive. 396 */ 397 void defineTemporaryLocalVariable(final int fromSlot, final int toSlot) { 398 defineLocalVariable(fromSlot, toSlot); 399 } 400 401 private void defineLocalVariable(final int fromSlot, final int toSlot) { 402 assert !hasLoadsOnStack(fromSlot, toSlot); 403 assert fromSlot < toSlot; 404 symbolBoundary.clear(fromSlot, toSlot - 1); 405 symbolBoundary.set(toSlot - 1); 406 final int lastExisting = Math.min(toSlot, localVariableTypes.size()); 407 for(int i = fromSlot; i < lastExisting; ++i) { 408 localVariableTypes.set(i, Type.UNKNOWN); 409 } 410 for(int i = lastExisting; i < toSlot; ++i) { 411 localVariableTypes.add(i, Type.UNKNOWN); 412 } 413 } 414 415 /** 416 * Undefines all local variables past the specified slot. 417 * @param fromSlot the first slot to be undefined 418 * @param canTruncateSymbol if false, the fromSlot must be either the first slot of a symbol, or the first slot 419 * after the last symbol. If true, the fromSlot can be in the middle of the storage area for a symbol. This 420 * should be used with care - it is only meant for use in optimism exception handlers. 421 */ 422 void undefineLocalVariables(final int fromSlot, final boolean canTruncateSymbol) { 423 final int lvarCount = localVariableTypes.size(); 424 assert lvarCount == symbolBoundary.length(); 425 assert !hasLoadsOnStack(fromSlot, lvarCount); 426 if(canTruncateSymbol) { 427 if(fromSlot > 0) { 428 symbolBoundary.set(fromSlot - 1); 429 } 430 } else { 431 assert fromSlot == 0 || symbolBoundary.get(fromSlot - 1); 432 } 433 if(fromSlot < lvarCount) { 434 symbolBoundary.clear(fromSlot, lvarCount); 435 localVariableTypes.subList(fromSlot, lvarCount).clear(); 436 } 437 firstTemp = Math.min(fromSlot, firstTemp); 438 assert symbolBoundary.length() == localVariableTypes.size(); 439 assert symbolBoundary.length() == fromSlot; 440 } 441 442 private void markAsOptimisticCatchHandler(final int liveLocalCount) { 443 // Live temporaries that are no longer on stack are undefined 444 undefineLocalVariables(liveLocalCount, true); 445 // Temporaries are promoted 446 firstTemp = liveLocalCount; 447 // No trailing undefineds 448 localVariableTypes.subList(firstTemp, localVariableTypes.size()).clear(); 449 assert symbolBoundary.length() == firstTemp; 450 // Generalize all reference types to Object, and promote boolean to int 451 for(final ListIterator<Type> it = localVariableTypes.listIterator(); it.hasNext();) { 452 final Type type = it.next(); 453 if(type == Type.BOOLEAN) { 454 it.set(Type.INT); 455 } else if(type.isObject() && type != Type.OBJECT) { 456 it.set(Type.OBJECT); 457 } 458 } 459 } 460 461 /** 462 * Returns true if any loads on the stack come from the specified slot range. 463 * @param fromSlot start of the range (inclusive) 464 * @param toSlot end of the range (exclusive) 465 * @return true if any loads on the stack come from the specified slot range. 466 */ 467 boolean hasLoadsOnStack(final int fromSlot, final int toSlot) { 468 for(int i = 0; i < sp; ++i) { 469 final int load = localLoads[i]; 470 if(load >= fromSlot && load < toSlot) { 471 return true; 472 } 473 } 474 return false; 475 } 476 477 @Override 478 public String toString() { 479 return "stack=" + Arrays.toString(Arrays.copyOf(data, sp)) 480 + ", symbolBoundaries=" + String.valueOf(symbolBoundary) 481 + ", firstTemp=" + firstTemp 482 + ", localTypes=" + String.valueOf(localVariableTypes) 483 ; 484 } 485 } 486 487 /** Next id for debugging purposes, remove if footprint becomes unmanageable */ 488 private static int nextId = 0; 489 490 /** Name of this label */ 491 private final String name; 492 493 /** Type stack at this label */ 494 private Label.Stack stack; 495 496 /** ASM representation of this label */ 497 private jdk.internal.org.objectweb.asm.Label label; 498 499 /** Id for debugging purposes, remove if footprint becomes unmanageable */ 500 private final int id; 501 502 /** Is this label reachable (anything ever jumped to it)? */ 503 private boolean reachable; 504 505 private boolean breakTarget; 506 507 /** 508 * Constructor 509 * 510 * @param name name of this label 511 */ 512 public Label(final String name) { 513 super(); 514 this.name = name; 515 this.id = nextId++; 516 } 517 518 /** 519 * Copy constructor 520 * 521 * @param label a label to clone 522 */ 523 public Label(final Label label) { 524 super(); 525 this.name = label.name; 526 this.id = label.id; 527 } 528 529 jdk.internal.org.objectweb.asm.Label getLabel() { 530 if (this.label == null) { 531 this.label = new jdk.internal.org.objectweb.asm.Label(); 532 } 533 return label; 534 } 535 536 Label.Stack getStack() { 537 return stack; 538 } 539 540 void joinFrom(final Label.Stack joinOrigin) { 541 this.reachable = true; 542 if(stack == null) { 543 stack = joinOrigin.clone(); 544 } else { 545 stack.joinFrom(joinOrigin, breakTarget); 546 } 547 } 548 549 void joinFromTry(final Label.Stack joinOrigin, final boolean isOptimismHandler) { 550 this.reachable = true; 551 if (stack == null) { 552 if(!isOptimismHandler) { 553 stack = joinOrigin.cloneWithEmptyStack(); 554 // Optimism handler needs temporaries to remain live, others don't. 555 stack.undefineLocalVariables(stack.firstTemp, false); 556 } 557 } else { 558 assert !isOptimismHandler; 559 stack.joinFromTry(joinOrigin); 560 } 561 } 562 563 void markAsBreakTarget() { 564 breakTarget = true; 565 } 566 567 boolean isBreakTarget() { 568 return breakTarget; 569 } 570 571 void onCatch() { 572 if(stack != null) { 573 stack = stack.cloneWithEmptyStack(); 574 } 575 } 576 void markAsOptimisticCatchHandler(final Label.Stack currentStack, final int liveLocalCount) { 577 stack = currentStack.cloneWithEmptyStack(); 578 stack.markAsOptimisticCatchHandler(liveLocalCount); 579 } 580 581 void markAsOptimisticContinuationHandlerFor(final Label afterConsumeStackLabel) { 582 stack = afterConsumeStackLabel.stack.cloneWithEmptyStack(); 583 } 584 585 boolean isReachable() { 586 return reachable; 587 } 588 589 boolean isAfter(final Label other) { 590 return label.getOffset() > other.label.getOffset(); 591 } 592 593 @Override 594 public String toString() { 595 return name + '_' + id; 596 } 597} 598