1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.dx.ssa; 18 19 import com.android.dx.rop.code.Exceptions; 20 import com.android.dx.rop.code.FillArrayDataInsn; 21 import com.android.dx.rop.code.Insn; 22 import com.android.dx.rop.code.PlainCstInsn; 23 import com.android.dx.rop.code.PlainInsn; 24 import com.android.dx.rop.code.RegOps; 25 import com.android.dx.rop.code.RegisterSpec; 26 import com.android.dx.rop.code.RegisterSpecList; 27 import com.android.dx.rop.code.Rop; 28 import com.android.dx.rop.code.Rops; 29 import com.android.dx.rop.code.ThrowingCstInsn; 30 import com.android.dx.rop.code.ThrowingInsn; 31 import com.android.dx.rop.cst.Constant; 32 import com.android.dx.rop.cst.CstLiteralBits; 33 import com.android.dx.rop.cst.CstMethodRef; 34 import com.android.dx.rop.cst.CstNat; 35 import com.android.dx.rop.cst.CstString; 36 import com.android.dx.rop.cst.CstType; 37 import com.android.dx.rop.cst.TypedConstant; 38 import com.android.dx.rop.cst.Zeroes; 39 import com.android.dx.rop.type.StdTypeList; 40 import com.android.dx.rop.type.Type; 41 import com.android.dx.rop.type.TypeBearer; 42 import java.util.ArrayList; 43 import java.util.BitSet; 44 import java.util.HashSet; 45 import java.util.List; 46 47 /** 48 * Simple intraprocedural escape analysis. Finds new arrays that don't escape 49 * the method they are created in and replaces the array values with registers. 50 */ 51 public class EscapeAnalysis { 52 /** 53 * Struct used to generate and maintain escape analysis results. 54 */ 55 static class EscapeSet { 56 /** set containing all registers related to an object */ 57 BitSet regSet; 58 /** escape state of the object */ 59 EscapeState escape; 60 /** list of objects that are put into this object */ 61 ArrayList<EscapeSet> childSets; 62 /** list of objects that this object is put into */ 63 ArrayList<EscapeSet> parentSets; 64 /** flag to indicate this object is a scalar replaceable array */ 65 boolean replaceableArray; 66 67 /** 68 * Constructs an instance of an EscapeSet 69 * 70 * @param reg the SSA register that defines the object 71 * @param size the number of registers in the method 72 * @param escState the lattice value to initially set this to 73 */ EscapeSet(int reg, int size, EscapeState escState)74 EscapeSet(int reg, int size, EscapeState escState) { 75 regSet = new BitSet(size); 76 regSet.set(reg); 77 escape = escState; 78 childSets = new ArrayList<EscapeSet>(); 79 parentSets = new ArrayList<EscapeSet>(); 80 replaceableArray = false; 81 } 82 } 83 84 /** 85 * Lattice values used to indicate escape state for an object. Analysis can 86 * only raise escape state values, not lower them. 87 * 88 * TOP - Used for objects that haven't been analyzed yet 89 * NONE - Object does not escape, and is eligible for scalar replacement. 90 * METHOD - Object remains local to method, but can't be scalar replaced. 91 * INTER - Object is passed between methods. (treated as globally escaping 92 * since this is an intraprocedural analysis) 93 * GLOBAL - Object escapes globally. 94 */ 95 public enum EscapeState { 96 TOP, NONE, METHOD, INTER, GLOBAL 97 } 98 99 /** method we're processing */ 100 private final SsaMethod ssaMeth; 101 /** ssaMeth.getRegCount() */ 102 private final int regCount; 103 /** Lattice values for each object register group */ 104 private final ArrayList<EscapeSet> latticeValues; 105 106 /** 107 * Constructs an instance. 108 * 109 * @param ssaMeth method to process 110 */ EscapeAnalysis(SsaMethod ssaMeth)111 private EscapeAnalysis(SsaMethod ssaMeth) { 112 this.ssaMeth = ssaMeth; 113 this.regCount = ssaMeth.getRegCount(); 114 this.latticeValues = new ArrayList<EscapeSet>(); 115 } 116 117 /** 118 * Finds the index in the lattice for a particular register. 119 * Returns the size of the lattice if the register wasn't found. 120 * 121 * @param reg {@code non-null;} register being looked up 122 * @return index of the register or size of the lattice if it wasn't found. 123 */ findSetIndex(RegisterSpec reg)124 private int findSetIndex(RegisterSpec reg) { 125 int i; 126 for (i = 0; i < latticeValues.size(); i++) { 127 EscapeSet e = latticeValues.get(i); 128 if (e.regSet.get(reg.getReg())) { 129 return i; 130 } 131 } 132 return i; 133 } 134 135 /** 136 * Finds the corresponding instruction for a given move result 137 * 138 * @param moveInsn {@code non-null;} a move result instruction 139 * @return {@code non-null;} the instruction that produces the result for 140 * the move 141 */ getInsnForMove(SsaInsn moveInsn)142 private SsaInsn getInsnForMove(SsaInsn moveInsn) { 143 int pred = moveInsn.getBlock().getPredecessors().nextSetBit(0); 144 ArrayList<SsaInsn> predInsns = ssaMeth.getBlocks().get(pred).getInsns(); 145 return predInsns.get(predInsns.size()-1); 146 } 147 148 /** 149 * Finds the corresponding move result for a given instruction 150 * 151 * @param insn {@code non-null;} an instruction that must always be 152 * followed by a move result 153 * @return {@code non-null;} the move result for the given instruction 154 */ getMoveForInsn(SsaInsn insn)155 private SsaInsn getMoveForInsn(SsaInsn insn) { 156 int succ = insn.getBlock().getSuccessors().nextSetBit(0); 157 ArrayList<SsaInsn> succInsns = ssaMeth.getBlocks().get(succ).getInsns(); 158 return succInsns.get(0); 159 } 160 161 /** 162 * Creates a link in the lattice between two EscapeSets due to a put 163 * instruction. The object being put is the child and the object being put 164 * into is the parent. A child set must always have an escape state at 165 * least as high as its parent. 166 * 167 * @param parentSet {@code non-null;} the EscapeSet for the object being put 168 * into 169 * @param childSet {@code non-null;} the EscapeSet for the object being put 170 */ addEdge(EscapeSet parentSet, EscapeSet childSet)171 private void addEdge(EscapeSet parentSet, EscapeSet childSet) { 172 if (!childSet.parentSets.contains(parentSet)) { 173 childSet.parentSets.add(parentSet); 174 } 175 if (!parentSet.childSets.contains(childSet)) { 176 parentSet.childSets.add(childSet); 177 } 178 } 179 180 /** 181 * Merges all links in the lattice among two EscapeSets. On return, the 182 * newNode will have its old links as well as all links from the oldNode. 183 * The oldNode has all its links removed. 184 * 185 * @param newNode {@code non-null;} the EscapeSet to merge all links into 186 * @param oldNode {@code non-null;} the EscapeSet to remove all links from 187 */ replaceNode(EscapeSet newNode, EscapeSet oldNode)188 private void replaceNode(EscapeSet newNode, EscapeSet oldNode) { 189 for (EscapeSet e : oldNode.parentSets) { 190 e.childSets.remove(oldNode); 191 e.childSets.add(newNode); 192 newNode.parentSets.add(e); 193 } 194 for (EscapeSet e : oldNode.childSets) { 195 e.parentSets.remove(oldNode); 196 e.parentSets.add(newNode); 197 newNode.childSets.add(e); 198 } 199 } 200 201 /** 202 * Performs escape analysis on a method. Finds scalar replaceable arrays and 203 * replaces them with equivalent registers. 204 * 205 * @param ssaMethod {@code non-null;} method to process 206 */ process(SsaMethod ssaMethod)207 public static void process(SsaMethod ssaMethod) { 208 new EscapeAnalysis(ssaMethod).run(); 209 } 210 211 /** 212 * Process a single instruction, looking for new objects resulting from 213 * move result or move param. 214 * 215 * @param insn {@code non-null;} instruction to process 216 */ processInsn(SsaInsn insn)217 private void processInsn(SsaInsn insn) { 218 int op = insn.getOpcode().getOpcode(); 219 RegisterSpec result = insn.getResult(); 220 EscapeSet escSet; 221 222 // Identify new objects 223 if (op == RegOps.MOVE_RESULT_PSEUDO && 224 result.getTypeBearer().getBasicType() == Type.BT_OBJECT) { 225 // Handle objects generated through move_result_pseudo 226 escSet = processMoveResultPseudoInsn(insn); 227 processRegister(result, escSet); 228 } else if (op == RegOps.MOVE_PARAM && 229 result.getTypeBearer().getBasicType() == Type.BT_OBJECT) { 230 // Track method arguments that are objects 231 escSet = new EscapeSet(result.getReg(), regCount, EscapeState.NONE); 232 latticeValues.add(escSet); 233 processRegister(result, escSet); 234 } else if (op == RegOps.MOVE_RESULT && 235 result.getTypeBearer().getBasicType() == Type.BT_OBJECT) { 236 // Track method return values that are objects 237 escSet = new EscapeSet(result.getReg(), regCount, EscapeState.NONE); 238 latticeValues.add(escSet); 239 processRegister(result, escSet); 240 } 241 } 242 243 /** 244 * Determine the origin of a move result pseudo instruction that generates 245 * an object. Creates a new EscapeSet for the new object accordingly. 246 * 247 * @param insn {@code non-null;} move result pseudo instruction to process 248 * @return {@code non-null;} an EscapeSet for the object referred to by the 249 * move result pseudo instruction 250 */ processMoveResultPseudoInsn(SsaInsn insn)251 private EscapeSet processMoveResultPseudoInsn(SsaInsn insn) { 252 RegisterSpec result = insn.getResult(); 253 SsaInsn prevSsaInsn = getInsnForMove(insn); 254 int prevOpcode = prevSsaInsn.getOpcode().getOpcode(); 255 EscapeSet escSet; 256 RegisterSpec prevSource; 257 258 switch(prevOpcode) { 259 // New instance / Constant 260 case RegOps.NEW_INSTANCE: 261 case RegOps.CONST: 262 escSet = new EscapeSet(result.getReg(), regCount, 263 EscapeState.NONE); 264 break; 265 // New array 266 case RegOps.NEW_ARRAY: 267 case RegOps.FILLED_NEW_ARRAY: 268 prevSource = prevSsaInsn.getSources().get(0); 269 if (prevSource.getTypeBearer().isConstant()) { 270 // New fixed array 271 escSet = new EscapeSet(result.getReg(), regCount, 272 EscapeState.NONE); 273 escSet.replaceableArray = true; 274 } else { 275 // New variable array 276 escSet = new EscapeSet(result.getReg(), regCount, 277 EscapeState.GLOBAL); 278 } 279 break; 280 // Loading a static object 281 case RegOps.GET_STATIC: 282 escSet = new EscapeSet(result.getReg(), regCount, 283 EscapeState.GLOBAL); 284 break; 285 // Type cast / load an object from a field or array 286 case RegOps.CHECK_CAST: 287 case RegOps.GET_FIELD: 288 case RegOps.AGET: 289 prevSource = prevSsaInsn.getSources().get(0); 290 int setIndex = findSetIndex(prevSource); 291 292 // Set should already exist, try to find it 293 if (setIndex != latticeValues.size()) { 294 escSet = latticeValues.get(setIndex); 295 escSet.regSet.set(result.getReg()); 296 return escSet; 297 } 298 299 // Set not found, must be either null or unknown 300 if (prevSource.getType() == Type.KNOWN_NULL) { 301 escSet = new EscapeSet(result.getReg(), regCount, 302 EscapeState.NONE); 303 } else { 304 escSet = new EscapeSet(result.getReg(), regCount, 305 EscapeState.GLOBAL); 306 } 307 break; 308 default: 309 return null; 310 } 311 312 // Add the newly created escSet to the lattice and return it 313 latticeValues.add(escSet); 314 return escSet; 315 } 316 317 /** 318 * Iterate through all the uses of a new object. 319 * 320 * @param result {@code non-null;} register where new object is stored 321 * @param escSet {@code non-null;} EscapeSet for the new object 322 */ processRegister(RegisterSpec result, EscapeSet escSet)323 private void processRegister(RegisterSpec result, EscapeSet escSet) { 324 ArrayList<RegisterSpec> regWorklist = new ArrayList<RegisterSpec>(); 325 regWorklist.add(result); 326 327 // Go through the worklist 328 while (!regWorklist.isEmpty()) { 329 int listSize = regWorklist.size() - 1; 330 RegisterSpec def = regWorklist.remove(listSize); 331 List<SsaInsn> useList = ssaMeth.getUseListForRegister(def.getReg()); 332 333 // Handle all the uses of this register 334 for (SsaInsn use : useList) { 335 Rop useOpcode = use.getOpcode(); 336 337 if (useOpcode == null) { 338 // Handle phis 339 processPhiUse(use, escSet, regWorklist); 340 } else { 341 // Handle other opcodes 342 processUse(def, use, escSet, regWorklist); 343 } 344 } 345 } 346 } 347 348 /** 349 * Handles phi uses of new objects. Will merge together the sources of a phi 350 * into a single EscapeSet. Adds the result of the phi to the worklist so 351 * its uses can be followed. 352 * 353 * @param use {@code non-null;} phi use being processed 354 * @param escSet {@code non-null;} EscapeSet for the object 355 * @param regWorklist {@code non-null;} worklist of instructions left to 356 * process for this object 357 */ processPhiUse(SsaInsn use, EscapeSet escSet, ArrayList<RegisterSpec> regWorklist)358 private void processPhiUse(SsaInsn use, EscapeSet escSet, 359 ArrayList<RegisterSpec> regWorklist) { 360 int setIndex = findSetIndex(use.getResult()); 361 if (setIndex != latticeValues.size()) { 362 // Check if result is in a set already 363 EscapeSet mergeSet = latticeValues.get(setIndex); 364 if (mergeSet != escSet) { 365 // If it is, merge the sets and states, then delete the copy 366 escSet.replaceableArray = false; 367 escSet.regSet.or(mergeSet.regSet); 368 if (escSet.escape.compareTo(mergeSet.escape) < 0) { 369 escSet.escape = mergeSet.escape; 370 } 371 replaceNode(escSet, mergeSet); 372 latticeValues.remove(setIndex); 373 } 374 } else { 375 // If no set is found, add it to this escSet and the worklist 376 escSet.regSet.set(use.getResult().getReg()); 377 regWorklist.add(use.getResult()); 378 } 379 } 380 381 /** 382 * Handles non-phi uses of new objects. Checks to see how instruction is 383 * used and updates the escape state accordingly. 384 * 385 * @param def {@code non-null;} register holding definition of new object 386 * @param use {@code non-null;} use of object being processed 387 * @param escSet {@code non-null;} EscapeSet for the object 388 * @param regWorklist {@code non-null;} worklist of instructions left to 389 * process for this object 390 */ processUse(RegisterSpec def, SsaInsn use, EscapeSet escSet, ArrayList<RegisterSpec> regWorklist)391 private void processUse(RegisterSpec def, SsaInsn use, EscapeSet escSet, 392 ArrayList<RegisterSpec> regWorklist) { 393 int useOpcode = use.getOpcode().getOpcode(); 394 switch (useOpcode) { 395 case RegOps.MOVE: 396 // Follow uses of the move by adding it to the worklist 397 escSet.regSet.set(use.getResult().getReg()); 398 regWorklist.add(use.getResult()); 399 break; 400 case RegOps.IF_EQ: 401 case RegOps.IF_NE: 402 case RegOps.CHECK_CAST: 403 // Compared objects can't be replaced, so promote if necessary 404 if (escSet.escape.compareTo(EscapeState.METHOD) < 0) { 405 escSet.escape = EscapeState.METHOD; 406 } 407 break; 408 case RegOps.APUT: 409 // For array puts, check for a constant array index 410 RegisterSpec putIndex = use.getSources().get(2); 411 if (!putIndex.getTypeBearer().isConstant()) { 412 // If not constant, array can't be replaced 413 escSet.replaceableArray = false; 414 } 415 // Intentional fallthrough 416 case RegOps.PUT_FIELD: 417 // Skip non-object puts 418 RegisterSpec putValue = use.getSources().get(0); 419 if (putValue.getTypeBearer().getBasicType() != Type.BT_OBJECT) { 420 break; 421 } 422 escSet.replaceableArray = false; 423 424 // Raise 1st object's escape state to 2nd if 2nd is higher 425 RegisterSpecList sources = use.getSources(); 426 if (sources.get(0).getReg() == def.getReg()) { 427 int setIndex = findSetIndex(sources.get(1)); 428 if (setIndex != latticeValues.size()) { 429 EscapeSet parentSet = latticeValues.get(setIndex); 430 addEdge(parentSet, escSet); 431 if (escSet.escape.compareTo(parentSet.escape) < 0) { 432 escSet.escape = parentSet.escape; 433 } 434 } 435 } else { 436 int setIndex = findSetIndex(sources.get(0)); 437 if (setIndex != latticeValues.size()) { 438 EscapeSet childSet = latticeValues.get(setIndex); 439 addEdge(escSet, childSet); 440 if (childSet.escape.compareTo(escSet.escape) < 0) { 441 childSet.escape = escSet.escape; 442 } 443 } 444 } 445 break; 446 case RegOps.AGET: 447 // For array gets, check for a constant array index 448 RegisterSpec getIndex = use.getSources().get(1); 449 if (!getIndex.getTypeBearer().isConstant()) { 450 // If not constant, array can't be replaced 451 escSet.replaceableArray = false; 452 } 453 break; 454 case RegOps.PUT_STATIC: 455 // Static puts cause an object to escape globally 456 escSet.escape = EscapeState.GLOBAL; 457 break; 458 case RegOps.INVOKE_STATIC: 459 case RegOps.INVOKE_VIRTUAL: 460 case RegOps.INVOKE_SUPER: 461 case RegOps.INVOKE_DIRECT: 462 case RegOps.INVOKE_INTERFACE: 463 case RegOps.RETURN: 464 case RegOps.THROW: 465 // These operations cause an object to escape interprocedurally 466 escSet.escape = EscapeState.INTER; 467 break; 468 default: 469 break; 470 } 471 } 472 473 /** 474 * Performs scalar replacement on all eligible arrays. 475 */ scalarReplacement()476 private void scalarReplacement() { 477 // Iterate through lattice, looking for non-escaping replaceable arrays 478 for (EscapeSet escSet : latticeValues) { 479 if (!escSet.replaceableArray || escSet.escape != EscapeState.NONE) { 480 continue; 481 } 482 483 // Get the instructions for the definition and move of the array 484 int e = escSet.regSet.nextSetBit(0); 485 SsaInsn def = ssaMeth.getDefinitionForRegister(e); 486 SsaInsn prev = getInsnForMove(def); 487 488 // Create a map for the new registers that will be created 489 TypeBearer lengthReg = prev.getSources().get(0).getTypeBearer(); 490 int length = ((CstLiteralBits) lengthReg).getIntBits(); 491 ArrayList<RegisterSpec> newRegs = 492 new ArrayList<RegisterSpec>(length); 493 HashSet<SsaInsn> deletedInsns = new HashSet<SsaInsn>(); 494 495 // Replace the definition of the array with registers 496 replaceDef(def, prev, length, newRegs); 497 498 // Mark definition instructions for deletion 499 deletedInsns.add(prev); 500 deletedInsns.add(def); 501 502 // Go through all uses of the array 503 List<SsaInsn> useList = ssaMeth.getUseListForRegister(e); 504 for (SsaInsn use : useList) { 505 // Replace the use with scalars and then mark it for deletion 506 replaceUse(use, prev, newRegs, deletedInsns); 507 deletedInsns.add(use); 508 } 509 510 // Delete all marked instructions 511 ssaMeth.deleteInsns(deletedInsns); 512 ssaMeth.onInsnsChanged(); 513 514 // Convert the method back to SSA form 515 SsaConverter.updateSsaMethod(ssaMeth, regCount); 516 517 // Propagate and remove extra moves added by scalar replacement 518 movePropagate(); 519 } 520 } 521 522 /** 523 * Replaces the instructions that define an array with equivalent registers. 524 * For each entry in the array, a register is created, initialized to zero. 525 * A mapping between this register and the corresponding array index is 526 * added. 527 * 528 * @param def {@code non-null;} move result instruction for array 529 * @param prev {@code non-null;} instruction for instantiating new array 530 * @param length size of the new array 531 * @param newRegs {@code non-null;} mapping of array indices to new 532 * registers to be populated 533 */ replaceDef(SsaInsn def, SsaInsn prev, int length, ArrayList<RegisterSpec> newRegs)534 private void replaceDef(SsaInsn def, SsaInsn prev, int length, 535 ArrayList<RegisterSpec> newRegs) { 536 Type resultType = def.getResult().getType(); 537 538 // Create new zeroed out registers for each element in the array 539 for (int i = 0; i < length; i++) { 540 Constant newZero = Zeroes.zeroFor(resultType.getComponentType()); 541 TypedConstant typedZero = (TypedConstant) newZero; 542 RegisterSpec newReg = 543 RegisterSpec.make(ssaMeth.makeNewSsaReg(), typedZero); 544 newRegs.add(newReg); 545 insertPlainInsnBefore(def, RegisterSpecList.EMPTY, newReg, 546 RegOps.CONST, newZero); 547 } 548 } 549 550 /** 551 * Replaces the use for a scalar replaceable array. Gets and puts become 552 * move instructions, and array lengths and fills are handled. Can also 553 * identify ArrayIndexOutOfBounds exceptions and throw them if detected. 554 * 555 * @param use {@code non-null;} move result instruction for array 556 * @param prev {@code non-null;} instruction for instantiating new array 557 * @param newRegs {@code non-null;} mapping of array indices to new 558 * registers 559 * @param deletedInsns {@code non-null;} set of instructions marked for 560 * deletion 561 */ replaceUse(SsaInsn use, SsaInsn prev, ArrayList<RegisterSpec> newRegs, HashSet<SsaInsn> deletedInsns)562 private void replaceUse(SsaInsn use, SsaInsn prev, 563 ArrayList<RegisterSpec> newRegs, 564 HashSet<SsaInsn> deletedInsns) { 565 int index; 566 int length = newRegs.size(); 567 SsaInsn next; 568 RegisterSpecList sources; 569 RegisterSpec source, result; 570 CstLiteralBits indexReg; 571 572 switch (use.getOpcode().getOpcode()) { 573 case RegOps.AGET: 574 // Replace array gets with moves 575 next = getMoveForInsn(use); 576 sources = use.getSources(); 577 indexReg = ((CstLiteralBits) sources.get(1).getTypeBearer()); 578 index = indexReg.getIntBits(); 579 if (index < length) { 580 source = newRegs.get(index); 581 result = source.withReg(next.getResult().getReg()); 582 insertPlainInsnBefore(next, RegisterSpecList.make(source), 583 result, RegOps.MOVE, null); 584 } else { 585 // Throw an exception if the index is out of bounds 586 insertExceptionThrow(next, sources.get(1), deletedInsns); 587 deletedInsns.add(next.getBlock().getInsns().get(2)); 588 } 589 deletedInsns.add(next); 590 break; 591 case RegOps.APUT: 592 // Replace array puts with moves 593 sources = use.getSources(); 594 indexReg = ((CstLiteralBits) sources.get(2).getTypeBearer()); 595 index = indexReg.getIntBits(); 596 if (index < length) { 597 source = sources.get(0); 598 result = source.withReg(newRegs.get(index).getReg()); 599 insertPlainInsnBefore(use, RegisterSpecList.make(source), 600 result, RegOps.MOVE, null); 601 // Update the newReg entry to mark value as unknown now 602 newRegs.set(index, result.withSimpleType()); 603 } else { 604 // Throw an exception if the index is out of bounds 605 insertExceptionThrow(use, sources.get(2), deletedInsns); 606 } 607 break; 608 case RegOps.ARRAY_LENGTH: 609 // Replace array lengths with const instructions 610 TypeBearer lengthReg = prev.getSources().get(0).getTypeBearer(); 611 //CstInteger lengthReg = CstInteger.make(length); 612 next = getMoveForInsn(use); 613 insertPlainInsnBefore(next, RegisterSpecList.EMPTY, 614 next.getResult(), RegOps.CONST, 615 (Constant) lengthReg); 616 deletedInsns.add(next); 617 break; 618 case RegOps.MARK_LOCAL: 619 // Remove mark local instructions 620 break; 621 case RegOps.FILL_ARRAY_DATA: 622 // Create const instructions for each fill value 623 Insn ropUse = use.getOriginalRopInsn(); 624 FillArrayDataInsn fill = (FillArrayDataInsn) ropUse; 625 ArrayList<Constant> constList = fill.getInitValues(); 626 for (int i = 0; i < length; i++) { 627 RegisterSpec newFill = 628 RegisterSpec.make(newRegs.get(i).getReg(), 629 (TypeBearer) constList.get(i)); 630 insertPlainInsnBefore(use, RegisterSpecList.EMPTY, newFill, 631 RegOps.CONST, constList.get(i)); 632 // Update the newRegs to hold the new const value 633 newRegs.set(i, newFill); 634 } 635 break; 636 default: 637 } 638 } 639 640 /** 641 * Identifies extra moves added by scalar replacement and propagates the 642 * source of the move to any users of the result. 643 */ movePropagate()644 private void movePropagate() { 645 for (int i = 0; i < ssaMeth.getRegCount(); i++) { 646 SsaInsn insn = ssaMeth.getDefinitionForRegister(i); 647 648 // Look for move instructions only 649 if (insn == null || insn.getOpcode() == null || 650 insn.getOpcode().getOpcode() != RegOps.MOVE) { 651 continue; 652 } 653 654 final ArrayList<SsaInsn>[] useList = ssaMeth.getUseListCopy(); 655 final RegisterSpec source = insn.getSources().get(0); 656 final RegisterSpec result = insn.getResult(); 657 658 // Ignore moves that weren't added due to scalar replacement 659 if (source.getReg() < regCount && result.getReg() < regCount) { 660 continue; 661 } 662 663 // Create a mapping from source to result 664 RegisterMapper mapper = new RegisterMapper() { 665 @Override 666 public int getNewRegisterCount() { 667 return ssaMeth.getRegCount(); 668 } 669 670 @Override 671 public RegisterSpec map(RegisterSpec registerSpec) { 672 if (registerSpec.getReg() == result.getReg()) { 673 return source; 674 } 675 676 return registerSpec; 677 } 678 }; 679 680 // Modify all uses of the move to use the source of the move instead 681 for (SsaInsn use : useList[result.getReg()]) { 682 use.mapSourceRegisters(mapper); 683 } 684 } 685 } 686 687 /** 688 * Runs escape analysis and scalar replacement of arrays. 689 */ run()690 private void run() { 691 ssaMeth.forEachBlockDepthFirstDom(new SsaBasicBlock.Visitor() { 692 @Override 693 public void visitBlock (SsaBasicBlock block, 694 SsaBasicBlock unused) { 695 block.forEachInsn(new SsaInsn.Visitor() { 696 @Override 697 public void visitMoveInsn(NormalSsaInsn insn) { 698 // do nothing 699 } 700 701 @Override 702 public void visitPhiInsn(PhiInsn insn) { 703 // do nothing 704 } 705 706 @Override 707 public void visitNonMoveInsn(NormalSsaInsn insn) { 708 processInsn(insn); 709 } 710 }); 711 } 712 }); 713 714 // Go through lattice and promote fieldSets as necessary 715 for (EscapeSet e : latticeValues) { 716 if (e.escape != EscapeState.NONE) { 717 for (EscapeSet field : e.childSets) { 718 if (e.escape.compareTo(field.escape) > 0) { 719 field.escape = e.escape; 720 } 721 } 722 } 723 } 724 725 // Perform scalar replacement for arrays 726 scalarReplacement(); 727 } 728 729 /** 730 * Replaces instructions that trigger an ArrayIndexOutofBounds exception 731 * with an actual throw of the exception. 732 * 733 * @param insn {@code non-null;} instruction causing the exception 734 * @param index {@code non-null;} index value that is out of bounds 735 * @param deletedInsns {@code non-null;} set of instructions marked for 736 * deletion 737 */ insertExceptionThrow(SsaInsn insn, RegisterSpec index, HashSet<SsaInsn> deletedInsns)738 private void insertExceptionThrow(SsaInsn insn, RegisterSpec index, 739 HashSet<SsaInsn> deletedInsns) { 740 // Create a new ArrayIndexOutOfBoundsException 741 CstType exception = 742 new CstType(Exceptions.TYPE_ArrayIndexOutOfBoundsException); 743 insertThrowingInsnBefore(insn, RegisterSpecList.EMPTY, null, 744 RegOps.NEW_INSTANCE, exception); 745 746 // Add a successor block with a move result pseudo for the exception 747 SsaBasicBlock currBlock = insn.getBlock(); 748 SsaBasicBlock newBlock = 749 currBlock.insertNewSuccessor(currBlock.getPrimarySuccessor()); 750 SsaInsn newInsn = newBlock.getInsns().get(0); 751 RegisterSpec newReg = 752 RegisterSpec.make(ssaMeth.makeNewSsaReg(), exception); 753 insertPlainInsnBefore(newInsn, RegisterSpecList.EMPTY, newReg, 754 RegOps.MOVE_RESULT_PSEUDO, null); 755 756 // Add another successor block to initialize the exception 757 SsaBasicBlock newBlock2 = 758 newBlock.insertNewSuccessor(newBlock.getPrimarySuccessor()); 759 SsaInsn newInsn2 = newBlock2.getInsns().get(0); 760 CstNat newNat = new CstNat(new CstString("<init>"), new CstString("(I)V")); 761 CstMethodRef newRef = new CstMethodRef(exception, newNat); 762 insertThrowingInsnBefore(newInsn2, RegisterSpecList.make(newReg, index), 763 null, RegOps.INVOKE_DIRECT, newRef); 764 deletedInsns.add(newInsn2); 765 766 // Add another successor block to throw the new exception 767 SsaBasicBlock newBlock3 = 768 newBlock2.insertNewSuccessor(newBlock2.getPrimarySuccessor()); 769 SsaInsn newInsn3 = newBlock3.getInsns().get(0); 770 insertThrowingInsnBefore(newInsn3, RegisterSpecList.make(newReg), null, 771 RegOps.THROW, null); 772 newBlock3.replaceSuccessor(newBlock3.getPrimarySuccessorIndex(), 773 ssaMeth.getExitBlock().getIndex()); 774 deletedInsns.add(newInsn3); 775 } 776 777 /** 778 * Inserts a new PlainInsn before the given instruction. 779 * TODO: move this somewhere more appropriate 780 * 781 * @param insn {@code non-null;} instruction to insert before 782 * @param newSources {@code non-null;} sources of new instruction 783 * @param newResult {@code non-null;} result of new instruction 784 * @param newOpcode opcode of new instruction 785 * @param cst {@code null-ok;} constant for new instruction, if any 786 */ insertPlainInsnBefore(SsaInsn insn, RegisterSpecList newSources, RegisterSpec newResult, int newOpcode, Constant cst)787 private void insertPlainInsnBefore(SsaInsn insn, 788 RegisterSpecList newSources, RegisterSpec newResult, int newOpcode, 789 Constant cst) { 790 791 Insn originalRopInsn = insn.getOriginalRopInsn(); 792 Rop newRop; 793 if (newOpcode == RegOps.MOVE_RESULT_PSEUDO) { 794 newRop = Rops.opMoveResultPseudo(newResult.getType()); 795 } else { 796 newRop = Rops.ropFor(newOpcode, newResult, newSources, cst); 797 } 798 799 Insn newRopInsn; 800 if (cst == null) { 801 newRopInsn = new PlainInsn(newRop, 802 originalRopInsn.getPosition(), newResult, newSources); 803 } else { 804 newRopInsn = new PlainCstInsn(newRop, 805 originalRopInsn.getPosition(), newResult, newSources, cst); 806 } 807 808 NormalSsaInsn newInsn = new NormalSsaInsn(newRopInsn, insn.getBlock()); 809 List<SsaInsn> insns = insn.getBlock().getInsns(); 810 811 insns.add(insns.lastIndexOf(insn), newInsn); 812 ssaMeth.onInsnAdded(newInsn); 813 } 814 815 /** 816 * Inserts a new ThrowingInsn before the given instruction. 817 * TODO: move this somewhere more appropriate 818 * 819 * @param insn {@code non-null;} instruction to insert before 820 * @param newSources {@code non-null;} sources of new instruction 821 * @param newResult {@code non-null;} result of new instruction 822 * @param newOpcode opcode of new instruction 823 * @param cst {@code null-ok;} constant for new instruction, if any 824 */ insertThrowingInsnBefore(SsaInsn insn, RegisterSpecList newSources, RegisterSpec newResult, int newOpcode, Constant cst)825 private void insertThrowingInsnBefore(SsaInsn insn, 826 RegisterSpecList newSources, RegisterSpec newResult, int newOpcode, 827 Constant cst) { 828 829 Insn origRopInsn = insn.getOriginalRopInsn(); 830 Rop newRop = Rops.ropFor(newOpcode, newResult, newSources, cst); 831 Insn newRopInsn; 832 if (cst == null) { 833 newRopInsn = new ThrowingInsn(newRop, 834 origRopInsn.getPosition(), newSources, StdTypeList.EMPTY); 835 } else { 836 newRopInsn = new ThrowingCstInsn(newRop, 837 origRopInsn.getPosition(), newSources, StdTypeList.EMPTY, cst); 838 } 839 840 NormalSsaInsn newInsn = new NormalSsaInsn(newRopInsn, insn.getBlock()); 841 List<SsaInsn> insns = insn.getBlock().getInsns(); 842 843 insns.add(insns.lastIndexOf(insn), newInsn); 844 ssaMeth.onInsnAdded(newInsn); 845 } 846 } 847