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 @Override 702 public void visitMoveInsn(NormalSsaInsn insn) { 703 processInsn(insn); 704 } 705 706 /** {@inheritDoc} */ 707 @Override 708 public void visitPhiInsn(PhiInsn insn) { 709 processInsn(insn); 710 } 711 712 /** {@inheritDoc} */ 713 @Override 714 public void visitNonMoveInsn(NormalSsaInsn insn) { 715 processInsn(insn); 716 } 717 718 /** 719 * This method collects three types of instructions: 720 * 721 * 1) Adds a local variable assignment to the 722 * {@code localVariables} map. 723 * 2) Add move-result-pseudo to the 724 * {@code moveResultPseudoInsns} list. 725 * 3) Add invoke-range to the 726 * {@code invokeRangeInsns} list. 727 * 728 * @param insn {@code non-null;} insn that may represent a 729 * local variable assignment 730 */ 731 private void processInsn(SsaInsn insn) { 732 RegisterSpec assignment; 733 assignment = insn.getLocalAssignment(); 734 735 if (assignment != null) { 736 LocalItem local = assignment.getLocalItem(); 737 738 ArrayList<RegisterSpec> regList 739 = localVariables.get(local); 740 741 if (regList == null) { 742 regList = new ArrayList<RegisterSpec>(); 743 localVariables.put(local, regList); 744 } 745 746 regList.add(assignment); 747 } 748 749 if (insn instanceof NormalSsaInsn) { 750 if (insn.getOpcode().getOpcode() == 751 RegOps.MOVE_RESULT_PSEUDO) { 752 moveResultPseudoInsns.add((NormalSsaInsn) insn); 753 } else if (Optimizer.getAdvice().requiresSourcesInOrder( 754 insn.getOriginalRopInsn().getOpcode(), 755 insn.getSources())) { 756 invokeRangeInsns.add((NormalSsaInsn) insn); 757 } 758 } else if (insn instanceof PhiInsn) { 759 phiInsns.add((PhiInsn) insn); 760 } 761 762 } 763 }); 764 } 765 766 /** 767 * Adds a mapping from an SSA register to a rop register. 768 * {@link #canMapReg} should have already been called. 769 * 770 * @param ssaSpec {@code non-null;} SSA register to map from 771 * @param ropReg {@code >=0;} rop register to map to 772 */ addMapping(RegisterSpec ssaSpec, int ropReg)773 private void addMapping(RegisterSpec ssaSpec, int ropReg) { 774 int ssaReg = ssaSpec.getReg(); 775 776 // An assertion. 777 if (ssaRegsMapped.get(ssaReg) || !canMapReg(ssaSpec, ropReg)) { 778 throw new RuntimeException( 779 "attempt to add invalid register mapping"); 780 } 781 782 if (DEBUG) { 783 System.out.printf("Add mapping s%d -> v%d c:%d\n", 784 ssaSpec.getReg(), ropReg, ssaSpec.getCategory()); 785 } 786 787 int category = ssaSpec.getCategory(); 788 mapper.addMapping(ssaSpec.getReg(), ropReg, category); 789 ssaRegsMapped.set(ssaReg); 790 usedRopRegs.set(ropReg, ropReg + category); 791 } 792 793 794 /** 795 * Maps the source registers of the specified instruction such that they 796 * will fall in a contiguous range in rop form. Moves are inserted as 797 * necessary to allow the range to be allocated. 798 * 799 * @param insn {@code non-null;} insn whos sources to process 800 */ adjustAndMapSourceRangeRange(NormalSsaInsn insn)801 private void adjustAndMapSourceRangeRange(NormalSsaInsn insn) { 802 int newRegStart = findRangeAndAdjust(insn); 803 804 RegisterSpecList sources = insn.getSources(); 805 int szSources = sources.size(); 806 int nextRopReg = newRegStart; 807 808 for (int i = 0; i < szSources; i++) { 809 RegisterSpec source = sources.get(i); 810 int sourceReg = source.getReg(); 811 int category = source.getCategory(); 812 int curRopReg = nextRopReg; 813 nextRopReg += category; 814 815 if (ssaRegsMapped.get(sourceReg)) { 816 continue; 817 } 818 819 LocalItem localItem = getLocalItemForReg(sourceReg); 820 addMapping(source, curRopReg); 821 822 if (localItem != null) { 823 markReserved(curRopReg, category); 824 ArrayList<RegisterSpec> similarRegisters 825 = localVariables.get(localItem); 826 827 int szSimilar = similarRegisters.size(); 828 829 /* 830 * Try to map all SSA registers also associated with 831 * this local. 832 */ 833 for (int j = 0; j < szSimilar; j++) { 834 RegisterSpec similarSpec = similarRegisters.get(j); 835 int similarReg = similarSpec.getReg(); 836 837 // Don't map anything that's also a source. 838 if (-1 != sources.indexOfRegister(similarReg)) { 839 continue; 840 } 841 842 // Registers left unmapped will get handled later. 843 tryMapReg(similarSpec, curRopReg, category); 844 } 845 } 846 } 847 } 848 849 /** 850 * Find a contiguous rop register range that fits the specified 851 * instruction's sources. First, try to center the range around 852 * sources that have already been mapped to rop registers. If that fails, 853 * just find a new contiguous range that doesn't interfere. 854 * 855 * @param insn {@code non-null;} the insn whose sources need to 856 * fit. Must be last insn in basic block. 857 * @return {@code >= 0;} rop register of start of range 858 */ findRangeAndAdjust(NormalSsaInsn insn)859 private int findRangeAndAdjust(NormalSsaInsn insn) { 860 RegisterSpecList sources = insn.getSources(); 861 int szSources = sources.size(); 862 // the category for each source index 863 int categoriesForIndex[] = new int[szSources]; 864 int rangeLength = 0; 865 866 // Compute rangeLength and categoriesForIndex 867 for (int i = 0; i < szSources; i++) { 868 int category = sources.get(i).getCategory(); 869 categoriesForIndex[i] = category; 870 rangeLength += categoriesForIndex[i]; 871 } 872 873 // the highest score of fits tried so far 874 int maxScore = Integer.MIN_VALUE; 875 // the high scoring range's start 876 int resultRangeStart = -1; 877 // by source index: set of sources needing moves in high scoring plan 878 BitSet resultMovesRequired = null; 879 880 /* 881 * First, go through each source that's already been mapped. Try 882 * to center the range around the rop register this source is mapped 883 * to. 884 */ 885 int rangeStartOffset = 0; 886 for (int i = 0; i < szSources; i++) { 887 int ssaCenterReg = sources.get(i).getReg(); 888 889 if (i != 0) { 890 rangeStartOffset -= categoriesForIndex[i - 1]; 891 } 892 if (!ssaRegsMapped.get(ssaCenterReg)) { 893 continue; 894 } 895 896 int rangeStart = mapper.oldToNew(ssaCenterReg) + rangeStartOffset; 897 898 if (rangeStart < 0 || spansParamRange(rangeStart, rangeLength)) { 899 continue; 900 } 901 902 BitSet curMovesRequired = new BitSet(szSources); 903 904 int fitWidth 905 = fitPlanForRange(rangeStart, insn, categoriesForIndex, 906 curMovesRequired); 907 908 if (fitWidth < 0) { 909 continue; 910 } 911 912 int score = fitWidth - curMovesRequired.cardinality(); 913 914 if (score > maxScore) { 915 maxScore = score; 916 resultRangeStart = rangeStart; 917 resultMovesRequired = curMovesRequired; 918 } 919 920 if (fitWidth == rangeLength) { 921 // We can't do any better than this, so stop here 922 break; 923 } 924 } 925 926 /* 927 * If we were unable to find a plan for a fit centered around 928 * an already-mapped source, just try to find a range of 929 * registers we can move the range into. 930 */ 931 932 if (resultRangeStart == -1) { 933 resultMovesRequired = new BitSet(szSources); 934 935 resultRangeStart = findAnyFittingRange(insn, rangeLength, 936 categoriesForIndex, resultMovesRequired); 937 } 938 939 /* 940 * Now, insert any moves required. 941 */ 942 943 for (int i = resultMovesRequired.nextSetBit(0); i >= 0; 944 i = resultMovesRequired.nextSetBit(i+1)) { 945 insn.changeOneSource(i, insertMoveBefore(insn, sources.get(i))); 946 } 947 948 return resultRangeStart; 949 } 950 951 /** 952 * Finds an unreserved range that will fit the sources of the 953 * specified instruction. Does not bother trying to center the range 954 * around an already-mapped source register; 955 * 956 * @param insn {@code non-null;} insn to build range for 957 * @param rangeLength {@code >=0;} length required in register units 958 * @param categoriesForIndex {@code non-null;} indexed by source index; 959 * the category for each source 960 * @param outMovesRequired {@code non-null;} an output parameter indexed by 961 * source index that will contain the set of sources which need 962 * moves inserted 963 * @return the rop register that starts the fitting range 964 */ findAnyFittingRange(NormalSsaInsn insn, int rangeLength, int[] categoriesForIndex, BitSet outMovesRequired)965 private int findAnyFittingRange(NormalSsaInsn insn, int rangeLength, 966 int[] categoriesForIndex, BitSet outMovesRequired) { 967 Alignment alignment = Alignment.UNSPECIFIED; 968 969 if (DexOptions.ALIGN_64BIT_REGS_SUPPORT) { 970 int regNumber = 0; 971 int p64bitsAligned = 0; 972 int p64bitsNotAligned = 0; 973 for (int category : categoriesForIndex) { 974 if (category == 2) { 975 if (isEven(regNumber)) { 976 p64bitsAligned++; 977 } else { 978 p64bitsNotAligned++; 979 } 980 regNumber += 2; 981 } else { 982 regNumber += 1; 983 } 984 } 985 986 if (p64bitsNotAligned > p64bitsAligned) { 987 if (isEven(paramRangeEnd)) { 988 alignment = Alignment.ODD; 989 } else { 990 alignment = Alignment.EVEN; 991 } 992 } else if (p64bitsAligned > 0) { 993 if (isEven(paramRangeEnd)) { 994 alignment = Alignment.EVEN; 995 } else { 996 alignment = Alignment.ODD; 997 } 998 } 999 } 1000 1001 int rangeStart = paramRangeEnd; 1002 while (true) { 1003 rangeStart = findNextUnreservedRopReg(rangeStart, rangeLength, alignment); 1004 1005 int fitWidth = fitPlanForRange(rangeStart, insn, categoriesForIndex, outMovesRequired); 1006 1007 if (fitWidth >= 0) { 1008 break; 1009 } 1010 rangeStart++; 1011 outMovesRequired.clear(); 1012 } 1013 1014 return rangeStart; 1015 } 1016 1017 /** 1018 * Attempts to build a plan for fitting a range of sources into rop 1019 * registers. 1020 * 1021 * @param ropReg {@code >= 0;} rop reg that begins range 1022 * @param insn {@code non-null;} insn to plan range for 1023 * @param categoriesForIndex {@code non-null;} indexed by source index; 1024 * the category for each source 1025 * @param outMovesRequired {@code non-null;} an output parameter indexed by 1026 * source index that will contain the set of sources which need 1027 * moves inserted 1028 * @return the width of the fit that that does not involve added moves or 1029 * {@code -1} if "no fit possible" 1030 */ fitPlanForRange(int ropReg, NormalSsaInsn insn, int[] categoriesForIndex, BitSet outMovesRequired)1031 private int fitPlanForRange(int ropReg, NormalSsaInsn insn, 1032 int[] categoriesForIndex, BitSet outMovesRequired) { 1033 RegisterSpecList sources = insn.getSources(); 1034 int szSources = sources.size(); 1035 int fitWidth = 0; 1036 IntSet liveOut = insn.getBlock().getLiveOutRegs(); 1037 RegisterSpecList liveOutSpecs = ssaSetToSpecs(liveOut); 1038 1039 // An SSA reg may only be mapped into a range once. 1040 BitSet seen = new BitSet(ssaMeth.getRegCount()); 1041 1042 for (int i = 0; i < szSources ; i++) { 1043 RegisterSpec ssaSpec = sources.get(i); 1044 int ssaReg = ssaSpec.getReg(); 1045 int category = categoriesForIndex[i]; 1046 1047 if (i != 0) { 1048 ropReg += categoriesForIndex[i-1]; 1049 } 1050 1051 if (ssaRegsMapped.get(ssaReg) 1052 && mapper.oldToNew(ssaReg) == ropReg) { 1053 // This is a register that is already mapped appropriately. 1054 fitWidth += category; 1055 } else if (rangeContainsReserved(ropReg, category)) { 1056 fitWidth = -1; 1057 break; 1058 } else if (!ssaRegsMapped.get(ssaReg) 1059 && canMapReg(ssaSpec, ropReg) 1060 && !seen.get(ssaReg)) { 1061 // This is a register that can be mapped appropriately. 1062 fitWidth += category; 1063 } else if (!mapper.areAnyPinned(liveOutSpecs, ropReg, category) 1064 && !mapper.areAnyPinned(sources, ropReg, category)) { 1065 /* 1066 * This is a source that can be moved. We can insert a 1067 * move as long as: 1068 * 1069 * * no SSA register pinned to the desired rop reg 1070 * is live out on the block 1071 * 1072 * * no SSA register pinned to desired rop reg is 1073 * a source of this insn (since this may require 1074 * overlapping moves, which we can't presently handle) 1075 */ 1076 1077 outMovesRequired.set(i); 1078 } else { 1079 fitWidth = -1; 1080 break; 1081 } 1082 1083 seen.set(ssaReg); 1084 } 1085 return fitWidth; 1086 } 1087 1088 /** 1089 * Converts a bit set of SSA registers into a RegisterSpecList containing 1090 * the definition specs of all the registers. 1091 * 1092 * @param ssaSet {@code non-null;} set of SSA registers 1093 * @return list of RegisterSpecs as noted above 1094 */ ssaSetToSpecs(IntSet ssaSet)1095 RegisterSpecList ssaSetToSpecs(IntSet ssaSet) { 1096 RegisterSpecList result = new RegisterSpecList(ssaSet.elements()); 1097 1098 IntIterator iter = ssaSet.iterator(); 1099 1100 int i = 0; 1101 while (iter.hasNext()) { 1102 result.set(i++, getDefinitionSpecForSsaReg(iter.next())); 1103 } 1104 1105 return result; 1106 } 1107 1108 /** 1109 * Gets a local item associated with an ssa register, if one exists. 1110 * 1111 * @param ssaReg {@code >= 0;} SSA register 1112 * @return {@code null-ok;} associated local item or null 1113 */ getLocalItemForReg(int ssaReg)1114 private LocalItem getLocalItemForReg(int ssaReg) { 1115 for (Map.Entry<LocalItem, ArrayList<RegisterSpec>> entry : 1116 localVariables.entrySet()) { 1117 for (RegisterSpec spec : entry.getValue()) { 1118 if (spec.getReg() == ssaReg) { 1119 return entry.getKey(); 1120 } 1121 } 1122 } 1123 1124 return null; 1125 } 1126 1127 /** 1128 * Attempts to map the sources and result of a phi to a common register. 1129 * Will try existing mappings first, from most to least common. If none 1130 * of the registers have mappings yet, a new mapping is created. 1131 */ processPhiInsn(PhiInsn insn)1132 private void processPhiInsn(PhiInsn insn) { 1133 RegisterSpec result = insn.getResult(); 1134 int resultReg = result.getReg(); 1135 int category = result.getCategory(); 1136 1137 RegisterSpecList sources = insn.getSources(); 1138 int sourcesSize = sources.size(); 1139 1140 // List of phi sources / result that need mapping 1141 ArrayList<RegisterSpec> ssaRegs = new ArrayList<RegisterSpec>(); 1142 1143 // Track how many times a particular mapping is found 1144 Multiset mapSet = new Multiset(sourcesSize + 1); 1145 1146 /* 1147 * If the result of the phi has an existing mapping, get it. 1148 * Otherwise, add it to the list of regs that need mapping. 1149 */ 1150 if (ssaRegsMapped.get(resultReg)) { 1151 mapSet.add(mapper.oldToNew(resultReg)); 1152 } else { 1153 ssaRegs.add(result); 1154 } 1155 1156 for (int i = 0; i < sourcesSize; i++) { 1157 RegisterSpec source = sources.get(i); 1158 SsaInsn def = ssaMeth.getDefinitionForRegister(source.getReg()); 1159 RegisterSpec sourceDef = def.getResult(); 1160 int sourceReg = sourceDef.getReg(); 1161 1162 /* 1163 * If a source of the phi has an existing mapping, get it. 1164 * Otherwise, add it to the list of regs that need mapping. 1165 */ 1166 if (ssaRegsMapped.get(sourceReg)) { 1167 mapSet.add(mapper.oldToNew(sourceReg)); 1168 } else { 1169 ssaRegs.add(sourceDef); 1170 } 1171 } 1172 1173 // Try all existing mappings, with the most common ones first 1174 for (int i = 0; i < mapSet.getSize(); i++) { 1175 int maxReg = mapSet.getAndRemoveHighestCount(); 1176 tryMapRegs(ssaRegs, maxReg, category, false); 1177 } 1178 1179 // Map any remaining unmapped regs with whatever fits 1180 int mapReg = findNextUnreservedRopReg(paramRangeEnd, category); 1181 while (!tryMapRegs(ssaRegs, mapReg, category, false)) { 1182 mapReg = findNextUnreservedRopReg(mapReg + 1, category); 1183 } 1184 } 1185 isEven(int regNumger)1186 private static boolean isEven(int regNumger) { 1187 return ((regNumger & 1) == 0); 1188 } 1189 1190 // A set that tracks how often elements are added to it. 1191 private static class Multiset { 1192 private final int[] reg; 1193 private final int[] count; 1194 private int size; 1195 1196 /** 1197 * Constructs an instance. 1198 * 1199 * @param maxSize the maximum distinct elements the set may have 1200 */ Multiset(int maxSize)1201 public Multiset(int maxSize) { 1202 reg = new int[maxSize]; 1203 count = new int[maxSize]; 1204 size = 0; 1205 } 1206 1207 /** 1208 * Adds an element to the set. 1209 * 1210 * @param element element to add 1211 */ add(int element)1212 public void add(int element) { 1213 for (int i = 0; i < size; i++) { 1214 if (reg[i] == element) { 1215 count[i]++; 1216 return; 1217 } 1218 } 1219 1220 reg[size] = element; 1221 count[size] = 1; 1222 size++; 1223 } 1224 1225 /** 1226 * Searches the set for the element that has been added the most. 1227 * In the case of a tie, the element that was added first is returned. 1228 * Then, it clears the count on that element. The size of the set 1229 * remains unchanged. 1230 * 1231 * @return element with the highest count 1232 */ getAndRemoveHighestCount()1233 public int getAndRemoveHighestCount() { 1234 int maxIndex = -1; 1235 int maxReg = -1; 1236 int maxCount = 0; 1237 1238 for (int i = 0; i < size; i++) { 1239 if (maxCount < count[i]) { 1240 maxIndex = i; 1241 maxReg = reg[i]; 1242 maxCount = count[i]; 1243 } 1244 } 1245 1246 count[maxIndex] = 0; 1247 return maxReg; 1248 } 1249 1250 /** 1251 * Gets the number of distinct elements in the set. 1252 * 1253 * @return size of the set 1254 */ getSize()1255 public int getSize() { 1256 return size; 1257 } 1258 } 1259 } 1260