1 /* 2 * Copyright (C) 2007 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.back; 18 19 import com.android.dx.dex.DexOptions; 20 import com.android.dx.rop.code.CstInsn; 21 import com.android.dx.rop.code.LocalItem; 22 import com.android.dx.rop.code.RegOps; 23 import com.android.dx.rop.code.RegisterSpec; 24 import com.android.dx.rop.code.RegisterSpecList; 25 import com.android.dx.rop.code.Rop; 26 import com.android.dx.rop.cst.CstInteger; 27 import com.android.dx.ssa.InterferenceRegisterMapper; 28 import com.android.dx.ssa.NormalSsaInsn; 29 import com.android.dx.ssa.Optimizer; 30 import com.android.dx.ssa.PhiInsn; 31 import com.android.dx.ssa.RegisterMapper; 32 import com.android.dx.ssa.SsaBasicBlock; 33 import com.android.dx.ssa.SsaInsn; 34 import com.android.dx.ssa.SsaMethod; 35 import com.android.dx.util.IntIterator; 36 import com.android.dx.util.IntSet; 37 import java.util.ArrayList; 38 import java.util.BitSet; 39 import java.util.Map; 40 import java.util.TreeMap; 41 42 /** 43 * Allocates registers in a first-fit fashion, with the bottom reserved for 44 * method parameters and all SSAregisters representing the same local variable 45 * kept together if possible. 46 */ 47 public class FirstFitLocalCombiningAllocator extends RegisterAllocator { 48 49 /** 50 * Alignment constraint that can be used during search of free registers. 51 */ 52 private enum Alignment { 53 EVEN { 54 @Override nextClearBit(BitSet bitSet, int startIdx)55 int nextClearBit(BitSet bitSet, int startIdx) { 56 int bitNumber = bitSet.nextClearBit(startIdx); 57 while (!isEven(bitNumber)) { 58 bitNumber = bitSet.nextClearBit(bitNumber + 1); 59 } 60 return bitNumber; 61 } 62 }, 63 ODD { 64 @Override nextClearBit(BitSet bitSet, int startIdx)65 int nextClearBit(BitSet bitSet, int startIdx) { 66 int bitNumber = bitSet.nextClearBit(startIdx); 67 while (isEven(bitNumber)) { 68 bitNumber = bitSet.nextClearBit(bitNumber + 1); 69 } 70 return bitNumber; 71 } 72 }, 73 UNSPECIFIED { 74 @Override nextClearBit(BitSet bitSet, int startIdx)75 int nextClearBit(BitSet bitSet, int startIdx) { 76 return bitSet.nextClearBit(startIdx); 77 } 78 }; 79 80 /** 81 * Returns the index of the first bit that is set to {@code false} that occurs on or after the 82 * specified starting index and that respect {@link Alignment}. 83 * 84 * @param bitSet bitSet working on. 85 * @param startIdx {@code >= 0;} the index to start checking from (inclusive). 86 * @return the index of the next clear bit respecting alignment. 87 */ nextClearBit(BitSet bitSet, int startIdx)88 abstract int nextClearBit(BitSet bitSet, int startIdx); 89 } 90 91 /** local debug flag */ 92 private static final boolean DEBUG = false; 93 94 /** maps local variable to a list of associated SSA registers */ 95 private final Map<LocalItem, ArrayList<RegisterSpec>> localVariables; 96 97 /** list of move-result-pesudo instructions seen in this method */ 98 private final ArrayList<NormalSsaInsn> moveResultPseudoInsns; 99 100 /** list of invoke-range instructions seen in this method */ 101 private final ArrayList<NormalSsaInsn> invokeRangeInsns; 102 103 /** list of phi instructions seen in this method */ 104 private final ArrayList<PhiInsn> phiInsns; 105 106 /** indexed by SSA reg; the set of SSA regs we've mapped */ 107 private final BitSet ssaRegsMapped; 108 109 /** Register mapper which will be our result */ 110 private final InterferenceRegisterMapper mapper; 111 112 /** end of rop registers range (starting at 0) reserved for parameters */ 113 private final int paramRangeEnd; 114 115 /** set of rop registers reserved for parameters or local variables */ 116 private final BitSet reservedRopRegs; 117 118 /** set of rop registers that have been used by anything */ 119 private final BitSet usedRopRegs; 120 121 /** true if converter should take steps to minimize rop-form registers */ 122 private final boolean minimizeRegisters; 123 124 /** 125 * Constructs instance. 126 * 127 * @param ssaMeth {@code non-null;} method to process 128 * @param interference non-null interference graph for SSA registers 129 * @param minimizeRegisters true if converter should take steps to 130 * minimize rop-form registers 131 */ FirstFitLocalCombiningAllocator( SsaMethod ssaMeth, InterferenceGraph interference, boolean minimizeRegisters)132 public FirstFitLocalCombiningAllocator( 133 SsaMethod ssaMeth, InterferenceGraph interference, 134 boolean minimizeRegisters) { 135 super(ssaMeth, interference); 136 137 ssaRegsMapped = new BitSet(ssaMeth.getRegCount()); 138 139 mapper = new InterferenceRegisterMapper( 140 interference, ssaMeth.getRegCount()); 141 142 this.minimizeRegisters = minimizeRegisters; 143 144 /* 145 * Reserve space for the params at the bottom of the register 146 * space. Later, we'll flip the params to the end of the register 147 * space. 148 */ 149 150 paramRangeEnd = ssaMeth.getParamWidth(); 151 152 reservedRopRegs = new BitSet(paramRangeEnd * 2); 153 reservedRopRegs.set(0, paramRangeEnd); 154 usedRopRegs = new BitSet(paramRangeEnd * 2); 155 localVariables = new TreeMap<LocalItem, ArrayList<RegisterSpec>>(); 156 moveResultPseudoInsns = new ArrayList<NormalSsaInsn>(); 157 invokeRangeInsns = new ArrayList<NormalSsaInsn>(); 158 phiInsns = new ArrayList<PhiInsn>(); 159 } 160 161 /** {@inheritDoc} */ 162 @Override wantsParamsMovedHigh()163 public boolean wantsParamsMovedHigh() { 164 return true; 165 } 166 167 /** {@inheritDoc} */ 168 @Override allocateRegisters()169 public RegisterMapper allocateRegisters() { 170 171 analyzeInstructions(); 172 173 if (DEBUG) { 174 printLocalVars(); 175 } 176 177 if (DEBUG) System.out.println("--->Mapping local-associated params"); 178 handleLocalAssociatedParams(); 179 180 if (DEBUG) System.out.println("--->Mapping other params"); 181 handleUnassociatedParameters(); 182 183 if (DEBUG) System.out.println("--->Mapping invoke-range"); 184 handleInvokeRangeInsns(); 185 186 if (DEBUG) { 187 System.out.println("--->Mapping local-associated non-params"); 188 } 189 handleLocalAssociatedOther(); 190 191 if (DEBUG) System.out.println("--->Mapping check-cast results"); 192 handleCheckCastResults(); 193 194 if (DEBUG) System.out.println("--->Mapping phis"); 195 handlePhiInsns(); 196 197 if (DEBUG) System.out.println("--->Mapping others"); 198 handleNormalUnassociated(); 199 200 return mapper; 201 } 202 203 /** 204 * Dumps local variable table to stdout for debugging. 205 */ printLocalVars()206 private void printLocalVars() { 207 System.out.println("Printing local vars"); 208 for (Map.Entry<LocalItem, ArrayList<RegisterSpec>> e : 209 localVariables.entrySet()) { 210 StringBuilder regs = new StringBuilder(); 211 212 regs.append('{'); 213 regs.append(' '); 214 for (RegisterSpec reg : e.getValue()) { 215 regs.append('v'); 216 regs.append(reg.getReg()); 217 regs.append(' '); 218 } 219 regs.append('}'); 220 System.out.printf("Local: %s Registers: %s\n", e.getKey(), regs); 221 } 222 } 223 224 /** 225 * Maps all local-associated parameters to rop registers. 226 */ handleLocalAssociatedParams()227 private void handleLocalAssociatedParams() { 228 for (ArrayList<RegisterSpec> ssaRegs : localVariables.values()) { 229 int sz = ssaRegs.size(); 230 int paramIndex = -1; 231 int paramCategory = 0; 232 233 // First, find out if this local variable is a parameter. 234 for (int i = 0; i < sz; i++) { 235 RegisterSpec ssaSpec = ssaRegs.get(i); 236 int ssaReg = ssaSpec.getReg(); 237 238 paramIndex = getParameterIndexForReg(ssaReg); 239 240 if (paramIndex >= 0) { 241 paramCategory = ssaSpec.getCategory(); 242 addMapping(ssaSpec, paramIndex); 243 break; 244 } 245 } 246 247 if (paramIndex < 0) { 248 // This local wasn't a parameter. 249 continue; 250 } 251 252 // Any remaining local-associated registers will be mapped later. 253 tryMapRegs(ssaRegs, paramIndex, paramCategory, true); 254 } 255 } 256 257 /** 258 * Gets the parameter index for SSA registers that are method parameters. 259 * {@code -1} is returned for non-parameter registers. 260 * 261 * @param ssaReg {@code >=0;} SSA register to look up 262 * @return parameter index or {@code -1} if not a parameter 263 */ getParameterIndexForReg(int ssaReg)264 private int getParameterIndexForReg(int ssaReg) { 265 SsaInsn defInsn = ssaMeth.getDefinitionForRegister(ssaReg); 266 if (defInsn == null) { 267 return -1; 268 } 269 270 Rop opcode = defInsn.getOpcode(); 271 272 // opcode == null for phi insns. 273 if (opcode != null && opcode.getOpcode() == RegOps.MOVE_PARAM) { 274 CstInsn origInsn = (CstInsn) defInsn.getOriginalRopInsn(); 275 return ((CstInteger) origInsn.getConstant()).getValue(); 276 } 277 278 return -1; 279 } 280 281 /** 282 * Maps all local-associated registers that are not parameters. 283 * Tries to find an unreserved range that's wide enough for all of 284 * the SSA registers, and then tries to map them all to that 285 * range. If not all fit, a new range is tried until all registers 286 * have been fit. 287 */ handleLocalAssociatedOther()288 private void handleLocalAssociatedOther() { 289 for (ArrayList<RegisterSpec> specs : localVariables.values()) { 290 int ropReg = paramRangeEnd; 291 292 boolean done = false; 293 do { 294 int maxCategory = 1; 295 296 // Compute max category for remaining unmapped registers. 297 int sz = specs.size(); 298 for (int i = 0; i < sz; i++) { 299 RegisterSpec ssaSpec = specs.get(i); 300 int category = ssaSpec.getCategory(); 301 if (!ssaRegsMapped.get(ssaSpec.getReg()) 302 && category > maxCategory) { 303 maxCategory = category; 304 } 305 } 306 307 ropReg = findRopRegForLocal(ropReg, maxCategory); 308 if (canMapRegs(specs, ropReg)) { 309 done = tryMapRegs(specs, ropReg, maxCategory, true); 310 } 311 312 // Increment for next call to findRopRegForLocal. 313 ropReg++; 314 } while (!done); 315 } 316 } 317 318 /** 319 * Tries to map a list of SSA registers into the a rop reg, marking 320 * used rop space as reserved. SSA registers that don't fit are left 321 * unmapped. 322 * 323 * @param specs {@code non-null;} SSA registers to attempt to map 324 * @param ropReg {@code >=0;} rop register to map to 325 * @param maxAllowedCategory {@code 1..2;} maximum category 326 * allowed in mapping. 327 * @param markReserved do so if {@code true} 328 * @return {@code true} if all registers were mapped, {@code false} 329 * if some remain unmapped 330 */ tryMapRegs( ArrayList<RegisterSpec> specs, int ropReg, int maxAllowedCategory, boolean markReserved)331 private boolean tryMapRegs( 332 ArrayList<RegisterSpec> specs, int ropReg, 333 int maxAllowedCategory, boolean markReserved) { 334 boolean remaining = false; 335 for (RegisterSpec spec : specs) { 336 if (ssaRegsMapped.get(spec.getReg())) { 337 continue; 338 } 339 340 boolean succeeded; 341 succeeded = tryMapReg(spec, ropReg, maxAllowedCategory); 342 remaining = !succeeded || remaining; 343 if (succeeded && markReserved) { 344 // This only needs to be called once really with 345 // the widest category used, but <shrug> 346 markReserved(ropReg, spec.getCategory()); 347 } 348 } 349 return !remaining; 350 } 351 352 /** 353 * Tries to map an SSA register to a rop register. 354 * 355 * @param ssaSpec {@code non-null;} SSA register 356 * @param ropReg {@code >=0;} rop register 357 * @param maxAllowedCategory {@code 1..2;} the maximum category 358 * that the SSA register is allowed to be 359 * @return {@code true} if map succeeded, {@code false} if not 360 */ tryMapReg(RegisterSpec ssaSpec, int ropReg, int maxAllowedCategory)361 private boolean tryMapReg(RegisterSpec ssaSpec, int ropReg, 362 int maxAllowedCategory) { 363 if (ssaSpec.getCategory() <= maxAllowedCategory 364 && !ssaRegsMapped.get(ssaSpec.getReg()) 365 && canMapReg(ssaSpec, ropReg)) { 366 addMapping(ssaSpec, ropReg); 367 return true; 368 } 369 370 return false; 371 } 372 373 /** 374 * Marks a range of rop registers as "reserved for a local variable." 375 * 376 * @param ropReg {@code >= 0;} rop register to reserve 377 * @param category {@code > 0;} width to reserve 378 */ markReserved(int ropReg, int category)379 private void markReserved(int ropReg, int category) { 380 reservedRopRegs.set(ropReg, ropReg + category, true); 381 } 382 383 /** 384 * Checks to see if any rop registers in the specified range are reserved 385 * for local variables or parameters. 386 * 387 * @param ropRangeStart {@code >= 0;} lowest rop register 388 * @param width {@code > 0;} number of rop registers in range. 389 * @return {@code true} if any register in range is marked reserved 390 */ rangeContainsReserved(int ropRangeStart, int width)391 private boolean rangeContainsReserved(int ropRangeStart, int width) { 392 for (int i = ropRangeStart; i < (ropRangeStart + width); i++) { 393 if (reservedRopRegs.get(i)) { 394 return true; 395 } 396 } 397 return false; 398 } 399 400 /** 401 * Returns true if given rop register represents the {@code this} pointer 402 * for a non-static method. 403 * 404 * @param startReg rop register 405 * @return true if the "this" pointer is located here. 406 */ isThisPointerReg(int startReg)407 private boolean isThisPointerReg(int startReg) { 408 // "this" is always the first parameter. 409 return startReg == 0 && !ssaMeth.isStatic(); 410 } 411 412 /** 413 * Return the register alignment constraint to have 64-bits registers that will be align on even 414 * dalvik registers after that parameter registers are move up to the top of the frame to match 415 * the calling convention. 416 * 417 * @param regCategory category of the register that will be aligned. 418 * @return the register alignment constraint. 419 */ getAlignment(int regCategory)420 private Alignment getAlignment(int regCategory) { 421 Alignment alignment = Alignment.UNSPECIFIED; 422 423 if (DexOptions.ALIGN_64BIT_REGS_SUPPORT && regCategory == 2) { 424 if (isEven(paramRangeEnd)) { 425 alignment = Alignment.EVEN; 426 } else { 427 alignment = Alignment.ODD; 428 } 429 } 430 431 return alignment; 432 } 433 434 /** 435 * Finds unreserved rop registers with a specific category. 436 * 437 * @param startReg {@code >= 0;} a rop register to start the search at 438 * @param regCategory {@code > 0;} category of the searched registers. 439 * @return {@code >= 0;} start of available registers. 440 */ findNextUnreservedRopReg(int startReg, int regCategory)441 private int findNextUnreservedRopReg(int startReg, int regCategory) { 442 return findNextUnreservedRopReg(startReg, regCategory, getAlignment(regCategory)); 443 } 444 445 /** 446 * Finds a range of unreserved rop registers. 447 * 448 * @param startReg {@code >= 0;} a rop register to start the search at 449 * @param width {@code > 0;} the width, in registers, required. 450 * @param alignment the alignment constraint. 451 * @return {@code >= 0;} start of available register range. 452 */ findNextUnreservedRopReg(int startReg, int width, Alignment alignment)453 private int findNextUnreservedRopReg(int startReg, int width, Alignment alignment) { 454 int reg = alignment.nextClearBit(reservedRopRegs, startReg); 455 456 while (true) { 457 int i = 1; 458 459 while (i < width && !reservedRopRegs.get(reg + i)) { 460 i++; 461 } 462 463 if (i == width) { 464 return reg; 465 } 466 467 reg = alignment.nextClearBit(reservedRopRegs, reg + i); 468 } 469 } 470 471 /** 472 * Finds rop registers that can be used for local variables. 473 * If {@code MIX_LOCALS_AND_OTHER} is {@code false}, this means any 474 * rop register that has not yet been used. 475 * 476 * @param startReg {@code >= 0;} a rop register to start the search at 477 * @param category {@code > 0;} the register category required. 478 * @return {@code >= 0;} start of available registers. 479 */ findRopRegForLocal(int startReg, int category)480 private int findRopRegForLocal(int startReg, int category) { 481 Alignment alignment = getAlignment(category); 482 int reg = alignment.nextClearBit(usedRopRegs, startReg); 483 484 while (true) { 485 int i = 1; 486 487 while (i < category && !usedRopRegs.get(reg + i)) { 488 i++; 489 } 490 491 if (i == category) { 492 return reg; 493 } 494 495 reg = alignment.nextClearBit(usedRopRegs, reg + i); 496 } 497 } 498 499 /** 500 * Maps any parameter that isn't local-associated, which can happen 501 * in the case where there is no java debug info. 502 */ handleUnassociatedParameters()503 private void handleUnassociatedParameters() { 504 int szSsaRegs = ssaMeth.getRegCount(); 505 506 for (int ssaReg = 0; ssaReg < szSsaRegs; ssaReg++) { 507 if (ssaRegsMapped.get(ssaReg)) { 508 // We already did this one above 509 continue; 510 } 511 512 int paramIndex = getParameterIndexForReg(ssaReg); 513 514 RegisterSpec ssaSpec = getDefinitionSpecForSsaReg(ssaReg); 515 if (paramIndex >= 0) { 516 addMapping(ssaSpec, paramIndex); 517 } 518 } 519 } 520 521 /** 522 * Handles all insns that want a register range for their sources. 523 */ handleInvokeRangeInsns()524 private void handleInvokeRangeInsns() { 525 for (NormalSsaInsn insn : invokeRangeInsns) { 526 adjustAndMapSourceRangeRange(insn); 527 } 528 } 529 530 /** 531 * Handles check cast results to reuse the same source register. 532 * Inserts a move if it can't map the same register to both and the 533 * check cast is not caught. 534 */ handleCheckCastResults()535 private void handleCheckCastResults() { 536 for (NormalSsaInsn insn : moveResultPseudoInsns) { 537 RegisterSpec moveRegSpec = insn.getResult(); 538 int moveReg = moveRegSpec.getReg(); 539 BitSet predBlocks = insn.getBlock().getPredecessors(); 540 541 // Expect one predecessor block only 542 if (predBlocks.cardinality() != 1) { 543 continue; 544 } 545 546 SsaBasicBlock predBlock = 547 ssaMeth.getBlocks().get(predBlocks.nextSetBit(0)); 548 ArrayList<SsaInsn> insnList = predBlock.getInsns(); 549 550 /** 551 * If the predecessor block has a check-cast, it will be the last 552 * instruction 553 */ 554 SsaInsn checkCastInsn = insnList.get(insnList.size() - 1); 555 if (checkCastInsn.getOpcode().getOpcode() != RegOps.CHECK_CAST) { 556 continue; 557 } 558 559 RegisterSpec checkRegSpec = checkCastInsn.getSources().get(0); 560 int checkReg = checkRegSpec.getReg(); 561 562 /** 563 * See if either register is already mapped. Most likely the move 564 * result will be mapped already since the cast result is stored 565 * in a local variable. 566 */ 567 int category = checkRegSpec.getCategory(); 568 boolean moveMapped = ssaRegsMapped.get(moveReg); 569 boolean checkMapped = ssaRegsMapped.get(checkReg); 570 if (moveMapped & !checkMapped) { 571 int moveRopReg = mapper.oldToNew(moveReg); 572 checkMapped = tryMapReg(checkRegSpec, moveRopReg, category); 573 } 574 if (checkMapped & !moveMapped) { 575 int checkRopReg = mapper.oldToNew(checkReg); 576 moveMapped = tryMapReg(moveRegSpec, checkRopReg, category); 577 } 578 579 // Map any unmapped registers to anything available 580 if (!moveMapped || !checkMapped) { 581 int ropReg = findNextUnreservedRopReg(paramRangeEnd, category); 582 ArrayList<RegisterSpec> ssaRegs = 583 new ArrayList<RegisterSpec>(2); 584 ssaRegs.add(moveRegSpec); 585 ssaRegs.add(checkRegSpec); 586 587 while (!tryMapRegs(ssaRegs, ropReg, category, false)) { 588 ropReg = findNextUnreservedRopReg(ropReg + 1, category); 589 } 590 } 591 592 /* 593 * If source and result have a different mapping, insert a move so 594 * they can have the same mapping. Don't do this if the check cast 595 * is caught, since it will overwrite a potentially live value. 596 */ 597 boolean hasExceptionHandlers = 598 checkCastInsn.getOriginalRopInsn().getCatches().size() != 0; 599 int moveRopReg = mapper.oldToNew(moveReg); 600 int checkRopReg = mapper.oldToNew(checkReg); 601 if (moveRopReg != checkRopReg && !hasExceptionHandlers) { 602 ((NormalSsaInsn) checkCastInsn).changeOneSource(0, 603 insertMoveBefore(checkCastInsn, checkRegSpec)); 604 addMapping(checkCastInsn.getSources().get(0), moveRopReg); 605 } 606 } 607 } 608 609 /** 610 * Handles all phi instructions, trying to map them to a common register. 611 */ handlePhiInsns()612 private void handlePhiInsns() { 613 for (PhiInsn insn : phiInsns) { 614 processPhiInsn(insn); 615 } 616 } 617 618 /** 619 * Maps all non-parameter, non-local variable registers. 620 */ handleNormalUnassociated()621 private void handleNormalUnassociated() { 622 int szSsaRegs = ssaMeth.getRegCount(); 623 624 for (int ssaReg = 0; ssaReg < szSsaRegs; ssaReg++) { 625 if (ssaRegsMapped.get(ssaReg)) { 626 // We already did this one 627 continue; 628 } 629 630 RegisterSpec ssaSpec = getDefinitionSpecForSsaReg(ssaReg); 631 632 if (ssaSpec == null) continue; 633 634 int category = ssaSpec.getCategory(); 635 // Find a rop reg that does not interfere 636 int ropReg = findNextUnreservedRopReg(paramRangeEnd, category); 637 while (!canMapReg(ssaSpec, ropReg)) { 638 ropReg = findNextUnreservedRopReg(ropReg + 1, category); 639 } 640 641 addMapping(ssaSpec, ropReg); 642 } 643 } 644 645 /** 646 * Checks to see if a list of SSA registers can all be mapped into 647 * the same rop reg. Ignores registers that have already been mapped, 648 * and checks the interference graph and ensures the range does not 649 * cross the parameter range. 650 * 651 * @param specs {@code non-null;} SSA registers to check 652 * @param ropReg {@code >=0;} rop register to check mapping to 653 * @return {@code true} if all unmapped registers can be mapped 654 */ canMapRegs(ArrayList<RegisterSpec> specs, int ropReg)655 private boolean canMapRegs(ArrayList<RegisterSpec> specs, int ropReg) { 656 for (RegisterSpec spec : specs) { 657 if (ssaRegsMapped.get(spec.getReg())) continue; 658 if (!canMapReg(spec, ropReg)) return false; 659 } 660 return true; 661 } 662 663 /** 664 * Checks to see if {@code ssaSpec} can be mapped to 665 * {@code ropReg}. Checks interference graph and ensures 666 * the range does not cross the parameter range. 667 * 668 * @param ssaSpec {@code non-null;} SSA spec 669 * @param ropReg prosepctive new-namespace reg 670 * @return {@code true} if mapping is possible 671 */ canMapReg(RegisterSpec ssaSpec, int ropReg)672 private boolean canMapReg(RegisterSpec ssaSpec, int ropReg) { 673 int category = ssaSpec.getCategory(); 674 return !(spansParamRange(ropReg, category) 675 || mapper.interferes(ssaSpec, ropReg)); 676 } 677 678 /** 679 * Returns true if the specified rop register + category 680 * will cross the boundry between the lower {@code paramWidth} 681 * registers reserved for method params and the upper registers. We cannot 682 * allocate a register that spans the param block and the normal block, 683 * because we will be moving the param block to high registers later. 684 * 685 * @param ssaReg register in new namespace 686 * @param category width that the register will have 687 * @return {@code true} in the case noted above 688 */ spansParamRange(int ssaReg, int category)689 private boolean spansParamRange(int ssaReg, int category) { 690 return ((ssaReg < paramRangeEnd) 691 && ((ssaReg + category) > paramRangeEnd)); 692 } 693 694 /** 695 * Analyze each instruction and find out all the local variable assignments 696 * and move-result-pseudo/invoke-range instrucitons. 697 */ analyzeInstructions()698 private void analyzeInstructions() { 699 ssaMeth.forEachInsn(new SsaInsn.Visitor() { 700 /** {@inheritDoc} */ 701 public void visitMoveInsn(NormalSsaInsn insn) { 702 processInsn(insn); 703 } 704 705 /** {@inheritDoc} */ 706 public void visitPhiInsn(PhiInsn insn) { 707 processInsn(insn); 708 } 709 710 /** {@inheritDoc} */ 711 public void visitNonMoveInsn(NormalSsaInsn insn) { 712 processInsn(insn); 713 } 714 715 /** 716 * This method collects three types of instructions: 717 * 718 * 1) Adds a local variable assignment to the 719 * {@code localVariables} map. 720 * 2) Add move-result-pseudo to the 721 * {@code moveResultPseudoInsns} list. 722 * 3) Add invoke-range to the 723 * {@code invokeRangeInsns} list. 724 * 725 * @param insn {@code non-null;} insn that may represent a 726 * local variable assignment 727 */ 728 private void processInsn(SsaInsn insn) { 729 RegisterSpec assignment; 730 assignment = insn.getLocalAssignment(); 731 732 if (assignment != null) { 733 LocalItem local = assignment.getLocalItem(); 734 735 ArrayList<RegisterSpec> regList 736 = localVariables.get(local); 737 738 if (regList == null) { 739 regList = new ArrayList<RegisterSpec>(); 740 localVariables.put(local, regList); 741 } 742 743 regList.add(assignment); 744 } 745 746 if (insn instanceof NormalSsaInsn) { 747 if (insn.getOpcode().getOpcode() == 748 RegOps.MOVE_RESULT_PSEUDO) { 749 moveResultPseudoInsns.add((NormalSsaInsn) insn); 750 } else if (Optimizer.getAdvice().requiresSourcesInOrder( 751 insn.getOriginalRopInsn().getOpcode(), 752 insn.getSources())) { 753 invokeRangeInsns.add((NormalSsaInsn) insn); 754 } 755 } else if (insn instanceof PhiInsn) { 756 phiInsns.add((PhiInsn) insn); 757 } 758 759 } 760 }); 761 } 762 763 /** 764 * Adds a mapping from an SSA register to a rop register. 765 * {@link #canMapReg} should have already been called. 766 * 767 * @param ssaSpec {@code non-null;} SSA register to map from 768 * @param ropReg {@code >=0;} rop register to map to 769 */ addMapping(RegisterSpec ssaSpec, int ropReg)770 private void addMapping(RegisterSpec ssaSpec, int ropReg) { 771 int ssaReg = ssaSpec.getReg(); 772 773 // An assertion. 774 if (ssaRegsMapped.get(ssaReg) || !canMapReg(ssaSpec, ropReg)) { 775 throw new RuntimeException( 776 "attempt to add invalid register mapping"); 777 } 778 779 if (DEBUG) { 780 System.out.printf("Add mapping s%d -> v%d c:%d\n", 781 ssaSpec.getReg(), ropReg, ssaSpec.getCategory()); 782 } 783 784 int category = ssaSpec.getCategory(); 785 mapper.addMapping(ssaSpec.getReg(), ropReg, category); 786 ssaRegsMapped.set(ssaReg); 787 usedRopRegs.set(ropReg, ropReg + category); 788 } 789 790 791 /** 792 * Maps the source registers of the specified instruction such that they 793 * will fall in a contiguous range in rop form. Moves are inserted as 794 * necessary to allow the range to be allocated. 795 * 796 * @param insn {@code non-null;} insn whos sources to process 797 */ adjustAndMapSourceRangeRange(NormalSsaInsn insn)798 private void adjustAndMapSourceRangeRange(NormalSsaInsn insn) { 799 int newRegStart = findRangeAndAdjust(insn); 800 801 RegisterSpecList sources = insn.getSources(); 802 int szSources = sources.size(); 803 int nextRopReg = newRegStart; 804 805 for (int i = 0; i < szSources; i++) { 806 RegisterSpec source = sources.get(i); 807 int sourceReg = source.getReg(); 808 int category = source.getCategory(); 809 int curRopReg = nextRopReg; 810 nextRopReg += category; 811 812 if (ssaRegsMapped.get(sourceReg)) { 813 continue; 814 } 815 816 LocalItem localItem = getLocalItemForReg(sourceReg); 817 addMapping(source, curRopReg); 818 819 if (localItem != null) { 820 markReserved(curRopReg, category); 821 ArrayList<RegisterSpec> similarRegisters 822 = localVariables.get(localItem); 823 824 int szSimilar = similarRegisters.size(); 825 826 /* 827 * Try to map all SSA registers also associated with 828 * this local. 829 */ 830 for (int j = 0; j < szSimilar; j++) { 831 RegisterSpec similarSpec = similarRegisters.get(j); 832 int similarReg = similarSpec.getReg(); 833 834 // Don't map anything that's also a source. 835 if (-1 != sources.indexOfRegister(similarReg)) { 836 continue; 837 } 838 839 // Registers left unmapped will get handled later. 840 tryMapReg(similarSpec, curRopReg, category); 841 } 842 } 843 } 844 } 845 846 /** 847 * Find a contiguous rop register range that fits the specified 848 * instruction's sources. First, try to center the range around 849 * sources that have already been mapped to rop registers. If that fails, 850 * just find a new contiguous range that doesn't interfere. 851 * 852 * @param insn {@code non-null;} the insn whose sources need to 853 * fit. Must be last insn in basic block. 854 * @return {@code >= 0;} rop register of start of range 855 */ findRangeAndAdjust(NormalSsaInsn insn)856 private int findRangeAndAdjust(NormalSsaInsn insn) { 857 RegisterSpecList sources = insn.getSources(); 858 int szSources = sources.size(); 859 // the category for each source index 860 int categoriesForIndex[] = new int[szSources]; 861 int rangeLength = 0; 862 863 // Compute rangeLength and categoriesForIndex 864 for (int i = 0; i < szSources; i++) { 865 int category = sources.get(i).getCategory(); 866 categoriesForIndex[i] = category; 867 rangeLength += categoriesForIndex[i]; 868 } 869 870 // the highest score of fits tried so far 871 int maxScore = Integer.MIN_VALUE; 872 // the high scoring range's start 873 int resultRangeStart = -1; 874 // by source index: set of sources needing moves in high scoring plan 875 BitSet resultMovesRequired = null; 876 877 /* 878 * First, go through each source that's already been mapped. Try 879 * to center the range around the rop register this source is mapped 880 * to. 881 */ 882 int rangeStartOffset = 0; 883 for (int i = 0; i < szSources; i++) { 884 int ssaCenterReg = sources.get(i).getReg(); 885 886 if (i != 0) { 887 rangeStartOffset -= categoriesForIndex[i - 1]; 888 } 889 if (!ssaRegsMapped.get(ssaCenterReg)) { 890 continue; 891 } 892 893 int rangeStart = mapper.oldToNew(ssaCenterReg) + rangeStartOffset; 894 895 if (rangeStart < 0 || spansParamRange(rangeStart, rangeLength)) { 896 continue; 897 } 898 899 BitSet curMovesRequired = new BitSet(szSources); 900 901 int fitWidth 902 = fitPlanForRange(rangeStart, insn, categoriesForIndex, 903 curMovesRequired); 904 905 if (fitWidth < 0) { 906 continue; 907 } 908 909 int score = fitWidth - curMovesRequired.cardinality(); 910 911 if (score > maxScore) { 912 maxScore = score; 913 resultRangeStart = rangeStart; 914 resultMovesRequired = curMovesRequired; 915 } 916 917 if (fitWidth == rangeLength) { 918 // We can't do any better than this, so stop here 919 break; 920 } 921 } 922 923 /* 924 * If we were unable to find a plan for a fit centered around 925 * an already-mapped source, just try to find a range of 926 * registers we can move the range into. 927 */ 928 929 if (resultRangeStart == -1) { 930 resultMovesRequired = new BitSet(szSources); 931 932 resultRangeStart = findAnyFittingRange(insn, rangeLength, 933 categoriesForIndex, resultMovesRequired); 934 } 935 936 /* 937 * Now, insert any moves required. 938 */ 939 940 for (int i = resultMovesRequired.nextSetBit(0); i >= 0; 941 i = resultMovesRequired.nextSetBit(i+1)) { 942 insn.changeOneSource(i, insertMoveBefore(insn, sources.get(i))); 943 } 944 945 return resultRangeStart; 946 } 947 948 /** 949 * Finds an unreserved range that will fit the sources of the 950 * specified instruction. Does not bother trying to center the range 951 * around an already-mapped source register; 952 * 953 * @param insn {@code non-null;} insn to build range for 954 * @param rangeLength {@code >=0;} length required in register units 955 * @param categoriesForIndex {@code non-null;} indexed by source index; 956 * the category for each source 957 * @param outMovesRequired {@code non-null;} an output parameter indexed by 958 * source index that will contain the set of sources which need 959 * moves inserted 960 * @return the rop register that starts the fitting range 961 */ findAnyFittingRange(NormalSsaInsn insn, int rangeLength, int[] categoriesForIndex, BitSet outMovesRequired)962 private int findAnyFittingRange(NormalSsaInsn insn, int rangeLength, 963 int[] categoriesForIndex, BitSet outMovesRequired) { 964 Alignment alignment = Alignment.UNSPECIFIED; 965 966 if (DexOptions.ALIGN_64BIT_REGS_SUPPORT) { 967 int regNumber = 0; 968 int p64bitsAligned = 0; 969 int p64bitsNotAligned = 0; 970 for (int category : categoriesForIndex) { 971 if (category == 2) { 972 if (isEven(regNumber)) { 973 p64bitsAligned++; 974 } else { 975 p64bitsNotAligned++; 976 } 977 regNumber += 2; 978 } else { 979 regNumber += 1; 980 } 981 } 982 983 if (p64bitsNotAligned > p64bitsAligned) { 984 if (isEven(paramRangeEnd)) { 985 alignment = Alignment.ODD; 986 } else { 987 alignment = Alignment.EVEN; 988 } 989 } else if (p64bitsAligned > 0) { 990 if (isEven(paramRangeEnd)) { 991 alignment = Alignment.EVEN; 992 } else { 993 alignment = Alignment.ODD; 994 } 995 } 996 } 997 998 int rangeStart = paramRangeEnd; 999 while (true) { 1000 rangeStart = findNextUnreservedRopReg(rangeStart, rangeLength, alignment); 1001 1002 int fitWidth = fitPlanForRange(rangeStart, insn, categoriesForIndex, outMovesRequired); 1003 1004 if (fitWidth >= 0) { 1005 break; 1006 } 1007 rangeStart++; 1008 outMovesRequired.clear(); 1009 } 1010 1011 return rangeStart; 1012 } 1013 1014 /** 1015 * Attempts to build a plan for fitting a range of sources into rop 1016 * registers. 1017 * 1018 * @param ropReg {@code >= 0;} rop reg that begins range 1019 * @param insn {@code non-null;} insn to plan range for 1020 * @param categoriesForIndex {@code non-null;} indexed by source index; 1021 * the category for each source 1022 * @param outMovesRequired {@code non-null;} an output parameter indexed by 1023 * source index that will contain the set of sources which need 1024 * moves inserted 1025 * @return the width of the fit that that does not involve added moves or 1026 * {@code -1} if "no fit possible" 1027 */ fitPlanForRange(int ropReg, NormalSsaInsn insn, int[] categoriesForIndex, BitSet outMovesRequired)1028 private int fitPlanForRange(int ropReg, NormalSsaInsn insn, 1029 int[] categoriesForIndex, BitSet outMovesRequired) { 1030 RegisterSpecList sources = insn.getSources(); 1031 int szSources = sources.size(); 1032 int fitWidth = 0; 1033 IntSet liveOut = insn.getBlock().getLiveOutRegs(); 1034 RegisterSpecList liveOutSpecs = ssaSetToSpecs(liveOut); 1035 1036 // An SSA reg may only be mapped into a range once. 1037 BitSet seen = new BitSet(ssaMeth.getRegCount()); 1038 1039 for (int i = 0; i < szSources ; i++) { 1040 RegisterSpec ssaSpec = sources.get(i); 1041 int ssaReg = ssaSpec.getReg(); 1042 int category = categoriesForIndex[i]; 1043 1044 if (i != 0) { 1045 ropReg += categoriesForIndex[i-1]; 1046 } 1047 1048 if (ssaRegsMapped.get(ssaReg) 1049 && mapper.oldToNew(ssaReg) == ropReg) { 1050 // This is a register that is already mapped appropriately. 1051 fitWidth += category; 1052 } else if (rangeContainsReserved(ropReg, category)) { 1053 fitWidth = -1; 1054 break; 1055 } else if (!ssaRegsMapped.get(ssaReg) 1056 && canMapReg(ssaSpec, ropReg) 1057 && !seen.get(ssaReg)) { 1058 // This is a register that can be mapped appropriately. 1059 fitWidth += category; 1060 } else if (!mapper.areAnyPinned(liveOutSpecs, ropReg, category) 1061 && !mapper.areAnyPinned(sources, ropReg, category)) { 1062 /* 1063 * This is a source that can be moved. We can insert a 1064 * move as long as: 1065 * 1066 * * no SSA register pinned to the desired rop reg 1067 * is live out on the block 1068 * 1069 * * no SSA register pinned to desired rop reg is 1070 * a source of this insn (since this may require 1071 * overlapping moves, which we can't presently handle) 1072 */ 1073 1074 outMovesRequired.set(i); 1075 } else { 1076 fitWidth = -1; 1077 break; 1078 } 1079 1080 seen.set(ssaReg); 1081 } 1082 return fitWidth; 1083 } 1084 1085 /** 1086 * Converts a bit set of SSA registers into a RegisterSpecList containing 1087 * the definition specs of all the registers. 1088 * 1089 * @param ssaSet {@code non-null;} set of SSA registers 1090 * @return list of RegisterSpecs as noted above 1091 */ ssaSetToSpecs(IntSet ssaSet)1092 RegisterSpecList ssaSetToSpecs(IntSet ssaSet) { 1093 RegisterSpecList result = new RegisterSpecList(ssaSet.elements()); 1094 1095 IntIterator iter = ssaSet.iterator(); 1096 1097 int i = 0; 1098 while (iter.hasNext()) { 1099 result.set(i++, getDefinitionSpecForSsaReg(iter.next())); 1100 } 1101 1102 return result; 1103 } 1104 1105 /** 1106 * Gets a local item associated with an ssa register, if one exists. 1107 * 1108 * @param ssaReg {@code >= 0;} SSA register 1109 * @return {@code null-ok;} associated local item or null 1110 */ getLocalItemForReg(int ssaReg)1111 private LocalItem getLocalItemForReg(int ssaReg) { 1112 for (Map.Entry<LocalItem, ArrayList<RegisterSpec>> entry : 1113 localVariables.entrySet()) { 1114 for (RegisterSpec spec : entry.getValue()) { 1115 if (spec.getReg() == ssaReg) { 1116 return entry.getKey(); 1117 } 1118 } 1119 } 1120 1121 return null; 1122 } 1123 1124 /** 1125 * Attempts to map the sources and result of a phi to a common register. 1126 * Will try existing mappings first, from most to least common. If none 1127 * of the registers have mappings yet, a new mapping is created. 1128 */ processPhiInsn(PhiInsn insn)1129 private void processPhiInsn(PhiInsn insn) { 1130 RegisterSpec result = insn.getResult(); 1131 int resultReg = result.getReg(); 1132 int category = result.getCategory(); 1133 1134 RegisterSpecList sources = insn.getSources(); 1135 int sourcesSize = sources.size(); 1136 1137 // List of phi sources / result that need mapping 1138 ArrayList<RegisterSpec> ssaRegs = new ArrayList<RegisterSpec>(); 1139 1140 // Track how many times a particular mapping is found 1141 Multiset mapSet = new Multiset(sourcesSize + 1); 1142 1143 /* 1144 * If the result of the phi has an existing mapping, get it. 1145 * Otherwise, add it to the list of regs that need mapping. 1146 */ 1147 if (ssaRegsMapped.get(resultReg)) { 1148 mapSet.add(mapper.oldToNew(resultReg)); 1149 } else { 1150 ssaRegs.add(result); 1151 } 1152 1153 for (int i = 0; i < sourcesSize; i++) { 1154 RegisterSpec source = sources.get(i); 1155 SsaInsn def = ssaMeth.getDefinitionForRegister(source.getReg()); 1156 RegisterSpec sourceDef = def.getResult(); 1157 int sourceReg = sourceDef.getReg(); 1158 1159 /* 1160 * If a source of the phi has an existing mapping, get it. 1161 * Otherwise, add it to the list of regs that need mapping. 1162 */ 1163 if (ssaRegsMapped.get(sourceReg)) { 1164 mapSet.add(mapper.oldToNew(sourceReg)); 1165 } else { 1166 ssaRegs.add(sourceDef); 1167 } 1168 } 1169 1170 // Try all existing mappings, with the most common ones first 1171 for (int i = 0; i < mapSet.getSize(); i++) { 1172 int maxReg = mapSet.getAndRemoveHighestCount(); 1173 tryMapRegs(ssaRegs, maxReg, category, false); 1174 } 1175 1176 // Map any remaining unmapped regs with whatever fits 1177 int mapReg = findNextUnreservedRopReg(paramRangeEnd, category); 1178 while (!tryMapRegs(ssaRegs, mapReg, category, false)) { 1179 mapReg = findNextUnreservedRopReg(mapReg + 1, category); 1180 } 1181 } 1182 isEven(int regNumger)1183 private static boolean isEven(int regNumger) { 1184 return ((regNumger & 1) == 0); 1185 } 1186 1187 // A set that tracks how often elements are added to it. 1188 private static class Multiset { 1189 private final int[] reg; 1190 private final int[] count; 1191 private int size; 1192 1193 /** 1194 * Constructs an instance. 1195 * 1196 * @param maxSize the maximum distinct elements the set may have 1197 */ Multiset(int maxSize)1198 public Multiset(int maxSize) { 1199 reg = new int[maxSize]; 1200 count = new int[maxSize]; 1201 size = 0; 1202 } 1203 1204 /** 1205 * Adds an element to the set. 1206 * 1207 * @param element element to add 1208 */ add(int element)1209 public void add(int element) { 1210 for (int i = 0; i < size; i++) { 1211 if (reg[i] == element) { 1212 count[i]++; 1213 return; 1214 } 1215 } 1216 1217 reg[size] = element; 1218 count[size] = 1; 1219 size++; 1220 } 1221 1222 /** 1223 * Searches the set for the element that has been added the most. 1224 * In the case of a tie, the element that was added first is returned. 1225 * Then, it clears the count on that element. The size of the set 1226 * remains unchanged. 1227 * 1228 * @return element with the highest count 1229 */ getAndRemoveHighestCount()1230 public int getAndRemoveHighestCount() { 1231 int maxIndex = -1; 1232 int maxReg = -1; 1233 int maxCount = 0; 1234 1235 for (int i = 0; i < size; i++) { 1236 if (maxCount < count[i]) { 1237 maxIndex = i; 1238 maxReg = reg[i]; 1239 maxCount = count[i]; 1240 } 1241 } 1242 1243 count[maxIndex] = 0; 1244 return maxReg; 1245 } 1246 1247 /** 1248 * Gets the number of distinct elements in the set. 1249 * 1250 * @return size of the set 1251 */ getSize()1252 public int getSize() { 1253 return size; 1254 } 1255 } 1256 } 1257