1 // Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file 2 // for details. All rights reserved. Use of this source code is governed by a 3 // BSD-style license that can be found in the LICENSE file. 4 package com.android.tools.r8.ir.conversion; 5 6 import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.ExcludeDexResources; 7 import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.IncludeAllResources; 8 9 import com.android.tools.r8.errors.Unreachable; 10 import com.android.tools.r8.graph.AppInfo; 11 import com.android.tools.r8.graph.AppInfoWithSubtyping; 12 import com.android.tools.r8.graph.Code; 13 import com.android.tools.r8.graph.DexApplication; 14 import com.android.tools.r8.graph.DexApplication.Builder; 15 import com.android.tools.r8.graph.DexEncodedMethod; 16 import com.android.tools.r8.graph.DexItemFactory; 17 import com.android.tools.r8.graph.DexMethod; 18 import com.android.tools.r8.graph.DexProgramClass; 19 import com.android.tools.r8.graph.DexString; 20 import com.android.tools.r8.graph.DexType; 21 import com.android.tools.r8.graph.GraphLense; 22 import com.android.tools.r8.ir.code.IRCode; 23 import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter; 24 import com.android.tools.r8.ir.desugar.LambdaRewriter; 25 import com.android.tools.r8.ir.optimize.CodeRewriter; 26 import com.android.tools.r8.ir.optimize.DeadCodeRemover; 27 import com.android.tools.r8.ir.optimize.Inliner; 28 import com.android.tools.r8.ir.optimize.Inliner.Constraint; 29 import com.android.tools.r8.ir.optimize.MemberValuePropagation; 30 import com.android.tools.r8.ir.optimize.Outliner; 31 import com.android.tools.r8.ir.optimize.PeepholeOptimizer; 32 import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator; 33 import com.android.tools.r8.ir.regalloc.RegisterAllocator; 34 import com.android.tools.r8.logging.Log; 35 import com.android.tools.r8.utils.CfgPrinter; 36 import com.android.tools.r8.utils.DescriptorUtils; 37 import com.android.tools.r8.utils.InternalOptions; 38 import com.android.tools.r8.utils.ThreadUtils; 39 import com.android.tools.r8.utils.Timing; 40 41 import com.google.common.collect.ImmutableSet; 42 import java.util.ArrayList; 43 import java.util.List; 44 import java.util.Set; 45 import java.util.concurrent.ExecutionException; 46 import java.util.concurrent.ExecutorService; 47 import java.util.concurrent.Executors; 48 import java.util.concurrent.Future; 49 import java.util.function.BiConsumer; 50 51 public class IRConverter { 52 53 public static final int PEEPHOLE_OPTIMIZATION_PASSES = 2; 54 55 private final Timing timing; 56 public final DexApplication application; 57 public final AppInfo appInfo; 58 private final Outliner outliner; 59 private final LambdaRewriter lambdaRewriter; 60 private final InterfaceMethodRewriter interfaceMethodRewriter; 61 private final InternalOptions options; 62 private final CfgPrinter printer; 63 private final GraphLense graphLense; 64 private final CodeRewriter codeRewriter; 65 private final MemberValuePropagation memberValuePropagation; 66 private final LensCodeRewriter lensCodeRewriter; 67 private final Inliner inliner; 68 private CallGraph callGraph; 69 private OptimizationFeedback ignoreOptimizationFeedback = new OptimizationFeedbackIgnore(); 70 71 private DexString highestSortingString; 72 IRConverter( Timing timing, DexApplication application, AppInfo appInfo, GraphLense graphLense, InternalOptions options, CfgPrinter printer, boolean enableDesugaring, boolean enableWholeProgramOptimizations)73 private IRConverter( 74 Timing timing, 75 DexApplication application, 76 AppInfo appInfo, 77 GraphLense graphLense, 78 InternalOptions options, 79 CfgPrinter printer, 80 boolean enableDesugaring, 81 boolean enableWholeProgramOptimizations) { 82 assert application != null; 83 assert appInfo != null; 84 assert options != null; 85 this.timing = timing != null ? timing : new Timing("internal"); 86 this.application = application; 87 this.appInfo = appInfo; 88 this.graphLense = graphLense != null ? graphLense : GraphLense.getIdentityLense(); 89 this.options = options; 90 this.printer = printer; 91 Set<DexType> libraryClassesWithOptimizationInfo = markLibraryMethodsReturningReceiver(); 92 this.codeRewriter = new CodeRewriter(appInfo, libraryClassesWithOptimizationInfo); 93 this.lambdaRewriter = enableDesugaring ? new LambdaRewriter(this) : null; 94 this.interfaceMethodRewriter = 95 (enableDesugaring && enableInterfaceMethodDesugaring()) 96 ? new InterfaceMethodRewriter(this) : null; 97 if (enableWholeProgramOptimizations) { 98 assert appInfo.withSubtyping() != null; 99 this.inliner = new Inliner(appInfo.withSubtyping(), graphLense, options); 100 this.outliner = new Outliner(appInfo, options); 101 this.memberValuePropagation = new MemberValuePropagation(appInfo); 102 this.lensCodeRewriter = new LensCodeRewriter(graphLense, appInfo.withSubtyping()); 103 } else { 104 this.inliner = null; 105 this.outliner = null; 106 this.memberValuePropagation = null; 107 this.lensCodeRewriter = null; 108 } 109 } 110 111 /** 112 * Create an IR converter for processing methods with full program optimization disabled. 113 */ IRConverter( DexApplication application, AppInfo appInfo, InternalOptions options)114 public IRConverter( 115 DexApplication application, 116 AppInfo appInfo, 117 InternalOptions options) { 118 this(null, application, appInfo, null, options, null, true, false); 119 } 120 121 /** 122 * Create an IR converter for processing methods without full program optimization enabled. 123 * 124 * The argument <code>enableDesugaring</code> if desugaing is enabled. 125 */ IRConverter( DexApplication application, AppInfo appInfo, InternalOptions options, boolean enableDesugaring)126 public IRConverter( 127 DexApplication application, 128 AppInfo appInfo, 129 InternalOptions options, 130 boolean enableDesugaring) { 131 this(null, application, appInfo, null, options, null, enableDesugaring, false); 132 } 133 134 /** 135 * Create an IR converter for processing methods with full program optimization disabled. 136 */ IRConverter( Timing timing, DexApplication application, AppInfo appInfo, InternalOptions options, CfgPrinter printer)137 public IRConverter( 138 Timing timing, 139 DexApplication application, 140 AppInfo appInfo, 141 InternalOptions options, 142 CfgPrinter printer) { 143 this(timing, application, appInfo, null, options, printer, true, false); 144 } 145 146 /** 147 * Create an IR converter for processing methods with full program optimization enabled. 148 */ IRConverter( Timing timing, DexApplication application, AppInfoWithSubtyping appInfo, InternalOptions options, CfgPrinter printer, GraphLense graphLense)149 public IRConverter( 150 Timing timing, 151 DexApplication application, 152 AppInfoWithSubtyping appInfo, 153 InternalOptions options, 154 CfgPrinter printer, 155 GraphLense graphLense) { 156 this(timing, application, appInfo, graphLense, options, printer, true, true); 157 } 158 enableInterfaceMethodDesugaring()159 private boolean enableInterfaceMethodDesugaring() { 160 switch (options.interfaceMethodDesugaring) { 161 case Off: 162 return false; 163 case Auto: 164 return !options.canUseDefaultAndStaticInterfaceMethods(); 165 } 166 throw new Unreachable(); 167 } 168 enableTryWithResourcesDesugaring()169 private boolean enableTryWithResourcesDesugaring() { 170 switch (options.tryWithResourcesDesugaring) { 171 case Off: 172 return false; 173 case Auto: 174 return !options.canUseSuppressedExceptions(); 175 } 176 throw new Unreachable(); 177 } 178 markLibraryMethodsReturningReceiver()179 private Set<DexType> markLibraryMethodsReturningReceiver() { 180 DexItemFactory dexItemFactory = appInfo.dexItemFactory; 181 dexItemFactory.stringBuilderMethods.forEachAppendMethod(this::markReturnsReceiver); 182 dexItemFactory.stringBufferMethods.forEachAppendMethod(this::markReturnsReceiver); 183 return ImmutableSet.of(dexItemFactory.stringBuilderType, dexItemFactory.stringBufferType); 184 } 185 markReturnsReceiver(DexMethod method)186 private void markReturnsReceiver(DexMethod method) { 187 DexEncodedMethod definition = appInfo.definitionFor(method); 188 if (definition != null) { 189 definition.markReturnsArgument(0); 190 } 191 } 192 removeLambdaDeserializationMethods()193 private void removeLambdaDeserializationMethods() { 194 if (lambdaRewriter != null) { 195 lambdaRewriter.removeLambdaDeserializationMethods(application.classes()); 196 } 197 } 198 synthesizeLambdaClasses(Builder builder)199 private void synthesizeLambdaClasses(Builder builder) { 200 if (lambdaRewriter != null) { 201 lambdaRewriter.adjustAccessibility(); 202 lambdaRewriter.synthesizeLambdaClasses(builder); 203 } 204 } 205 desugarInterfaceMethods( Builder builder, InterfaceMethodRewriter.Flavor includeAllResources)206 private void desugarInterfaceMethods( 207 Builder builder, InterfaceMethodRewriter.Flavor includeAllResources) { 208 if (interfaceMethodRewriter != null) { 209 interfaceMethodRewriter.desugarInterfaceMethods(builder, includeAllResources); 210 } 211 } 212 convertToDex(ExecutorService executor)213 public DexApplication convertToDex(ExecutorService executor) throws ExecutionException { 214 removeLambdaDeserializationMethods(); 215 216 convertClassesToDex(application.classes(), executor); 217 218 // Build a new application with jumbo string info, 219 Builder builder = new Builder(application); 220 builder.setHighestSortingString(highestSortingString); 221 222 synthesizeLambdaClasses(builder); 223 desugarInterfaceMethods(builder, ExcludeDexResources); 224 225 return builder.build(); 226 } 227 convertClassesToDex(Iterable<DexProgramClass> classes, ExecutorService executor)228 private void convertClassesToDex(Iterable<DexProgramClass> classes, 229 ExecutorService executor) throws ExecutionException { 230 List<Future<?>> futures = new ArrayList<>(); 231 for (DexProgramClass clazz : classes) { 232 futures.add(executor.submit(() -> { 233 convertMethodsToDex(clazz.directMethods()); 234 convertMethodsToDex(clazz.virtualMethods()); 235 })); 236 } 237 ThreadUtils.awaitFutures(futures); 238 } 239 convertMethodsToDex(DexEncodedMethod[] methods)240 private void convertMethodsToDex(DexEncodedMethod[] methods) { 241 for (int i = 0; i < methods.length; i++) { 242 DexEncodedMethod method = methods[i]; 243 if (method.getCode() != null) { 244 boolean matchesMethodFilter = options.methodMatchesFilter(method); 245 if (matchesMethodFilter) { 246 if (method.getCode().isJarCode()) { 247 rewriteCode(method, ignoreOptimizationFeedback, Outliner::noProcessing); 248 } 249 updateHighestSortingStrings(method); 250 } 251 } 252 } 253 } 254 optimize()255 public DexApplication optimize() throws ExecutionException { 256 ExecutorService executor = Executors.newSingleThreadExecutor(); 257 try { 258 return optimize(executor); 259 } finally { 260 executor.shutdown(); 261 } 262 } 263 optimize(ExecutorService executorService)264 public DexApplication optimize(ExecutorService executorService) throws ExecutionException { 265 removeLambdaDeserializationMethods(); 266 267 timing.begin("Build call graph"); 268 callGraph = CallGraph.build(application, appInfo.withSubtyping(), graphLense); 269 timing.end(); 270 271 // The process is in two phases. 272 // 1) Subject all DexEncodedMethods to optimization (except outlining). 273 // - a side effect is candidates for outlining are identified. 274 // 2) Perform outlining for the collected candidates. 275 // Ideally, we should outline eagerly when threshold for a template has been reached. 276 277 // Process the application identifying outlining candidates. 278 timing.begin("IR conversion phase 1"); 279 OptimizationFeedback directFeedback = new OptimizationFeedbackDirect(); 280 while (!callGraph.isEmpty()) { 281 List<DexEncodedMethod> methods = callGraph.extractLeaves(); 282 assert methods.size() > 0; 283 // For testing we have the option to determine the processing order of the methods. 284 if (options.testing.irOrdering != null) { 285 methods = options.testing.irOrdering.apply(methods); 286 } 287 List<Future<?>> futures = new ArrayList<>(); 288 for (DexEncodedMethod method : methods) { 289 futures.add(executorService.submit(() -> { 290 processMethod(method, directFeedback, 291 outliner == null ? Outliner::noProcessing : outliner::identifyCandidates); 292 })); 293 } 294 ThreadUtils.awaitFutures(futures); 295 } 296 timing.end(); 297 298 // Build a new application with jumbo string info. 299 Builder builder = new Builder(application); 300 builder.setHighestSortingString(highestSortingString); 301 302 // Second inlining pass for dealing with double inline callers. 303 if (inliner != null) { 304 inliner.processDoubleInlineCallers(this, ignoreOptimizationFeedback); 305 } 306 307 synthesizeLambdaClasses(builder); 308 desugarInterfaceMethods(builder, IncludeAllResources); 309 310 if (outliner != null) { 311 timing.begin("IR conversion phase 2"); 312 // Compile all classes flagged for outlining and 313 // add the outline support class IF needed. 314 DexProgramClass outlineClass = prepareOutlining(); 315 if (outlineClass != null) { 316 // Process the selected methods for outlining. 317 for (DexEncodedMethod method : outliner.getMethodsSelectedForOutlining()) { 318 // This is the second time we compile this method first mark it not processed. 319 assert !method.getCode().isOutlineCode(); 320 processMethod(method, ignoreOptimizationFeedback, outliner::applyOutliningCandidate); 321 assert method.isProcessed(); 322 } 323 builder.addSynthesizedClass(outlineClass, true); 324 clearDexMethodCompilationState(outlineClass); 325 } 326 timing.end(); 327 } 328 clearDexMethodCompilationState(); 329 return builder.build(); 330 } 331 processJumboStrings(DexEncodedMethod method, DexString firstJumboString)332 public void processJumboStrings(DexEncodedMethod method, DexString firstJumboString) { 333 convertMethodJumboStringsOnly(method, firstJumboString); 334 } 335 clearDexMethodCompilationState()336 private void clearDexMethodCompilationState() { 337 application.classes().forEach(this::clearDexMethodCompilationState); 338 } 339 clearDexMethodCompilationState(DexProgramClass clazz)340 private void clearDexMethodCompilationState(DexProgramClass clazz) { 341 clazz.forEachMethod(DexEncodedMethod::markNotProcessed); 342 } 343 344 /** 345 * This will replace the Dex code in the method with the Dex code generated from the provided IR. 346 * 347 * This method is *only* intended for testing, where tests manipulate the IR and need runnable Dex 348 * code. 349 * 350 * @param method the method to replace code for 351 * @param code the IR code for the method 352 */ replaceCodeForTesting(DexEncodedMethod method, IRCode code)353 public void replaceCodeForTesting(DexEncodedMethod method, IRCode code) { 354 if (Log.ENABLED) { 355 Log.debug(getClass(), "Initial (SSA) flow graph for %s:\n%s", method.toSourceString(), code); 356 } 357 assert code.isConsistentSSA(); 358 RegisterAllocator registerAllocator = performRegisterAllocation(code, method); 359 method.setCode(code, registerAllocator, appInfo.dexItemFactory); 360 if (Log.ENABLED) { 361 Log.debug(getClass(), "Resulting dex code for %s:\n%s", 362 method.toSourceString(), logCode(options, method)); 363 } 364 } 365 366 // Find an unused name for the outlining class. When multiple runs produces additional 367 // outlining the default outlining class might already be present. computeOutlineClassType()368 private DexType computeOutlineClassType() { 369 DexType result; 370 int count = 0; 371 do { 372 String name = options.outline.className + (count == 0 ? "" : Integer.toString(count)); 373 count++; 374 result = application.dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(name)); 375 } while (application.definitionFor(result) != null); 376 return result; 377 } 378 prepareOutlining()379 private DexProgramClass prepareOutlining() { 380 if (!outliner.selectMethodsForOutlining()) { 381 return null; 382 } 383 DexProgramClass outlineClass = outliner.buildOutlinerClass(computeOutlineClassType()); 384 optimizeSynthesizedClass(outlineClass); 385 return outlineClass; 386 } 387 optimizeSynthesizedClass(DexProgramClass clazz)388 public void optimizeSynthesizedClass(DexProgramClass clazz) { 389 // Process the generated class, but don't apply any outlining. 390 clazz.forEachMethod(this::optimizeSynthesizedMethod); 391 } 392 optimizeSynthesizedMethod(DexEncodedMethod method)393 public void optimizeSynthesizedMethod(DexEncodedMethod method) { 394 // Process the generated method, but don't apply any outlining. 395 processMethod(method, ignoreOptimizationFeedback, Outliner::noProcessing); 396 } 397 logCode(InternalOptions options, DexEncodedMethod method)398 private String logCode(InternalOptions options, DexEncodedMethod method) { 399 return options.useSmaliSyntax ? method.toSmaliString(null) : method.codeToString(); 400 } 401 processMethod(DexEncodedMethod method, OptimizationFeedback feedback, BiConsumer<IRCode, DexEncodedMethod> outlineHandler)402 public void processMethod(DexEncodedMethod method, 403 OptimizationFeedback feedback, 404 BiConsumer<IRCode, DexEncodedMethod> outlineHandler) { 405 Code code = method.getCode(); 406 boolean matchesMethodFilter = options.methodMatchesFilter(method); 407 if (code != null && matchesMethodFilter) { 408 rewriteCode(method, feedback, outlineHandler); 409 } else { 410 // Mark abstract methods as processed as well. 411 method.markProcessed(Constraint.NEVER); 412 } 413 } 414 rewriteCode(DexEncodedMethod method, OptimizationFeedback feedback, BiConsumer<IRCode, DexEncodedMethod> outlineHandler)415 private void rewriteCode(DexEncodedMethod method, 416 OptimizationFeedback feedback, 417 BiConsumer<IRCode, DexEncodedMethod> outlineHandler) { 418 if (options.verbose) { 419 System.out.println("Processing: " + method.toSourceString()); 420 } 421 if (Log.ENABLED) { 422 Log.debug(getClass(), "Original code for %s:\n%s", 423 method.toSourceString(), logCode(options, method)); 424 } 425 IRCode code = method.buildIR(options); 426 if (code == null) { 427 feedback.markProcessed(method, Constraint.NEVER); 428 return; 429 } 430 if (Log.ENABLED) { 431 Log.debug(getClass(), "Initial (SSA) flow graph for %s:\n%s", method.toSourceString(), code); 432 } 433 // Compilation header if printing CFGs for this method. 434 printC1VisualizerHeader(method); 435 printMethod(code, "Initial IR (SSA)"); 436 437 if (lensCodeRewriter != null) { 438 lensCodeRewriter.rewrite(code, method); 439 } else { 440 assert graphLense.isIdentityLense(); 441 } 442 if (memberValuePropagation != null) { 443 memberValuePropagation.rewriteWithConstantValues(code); 444 } 445 if (options.removeSwitchMaps) { 446 // TODO(zerny): Should we support removeSwitchMaps in debug mode? b/62936642 447 assert !options.debug; 448 codeRewriter.removeSwitchMaps(code); 449 } 450 if (options.disableAssertions) { 451 codeRewriter.disableAssertions(code); 452 } 453 if (options.inlineAccessors && inliner != null) { 454 // TODO(zerny): Should we support inlining in debug mode? b/62937285 455 assert !options.debug; 456 inliner.performInlining(method, code, callGraph); 457 } 458 codeRewriter.rewriteLongCompareAndRequireNonNull(code, options); 459 codeRewriter.commonSubexpressionElimination(code); 460 codeRewriter.simplifyArrayConstruction(code); 461 codeRewriter.rewriteMoveResult(code); 462 codeRewriter.splitConstants(code); 463 codeRewriter.foldConstants(code); 464 codeRewriter.rewriteSwitch(code); 465 codeRewriter.simplifyIf(code); 466 if (Log.ENABLED) { 467 Log.debug(getClass(), "Intermediate (SSA) flow graph for %s:\n%s", 468 method.toSourceString(), code); 469 } 470 // Dead code removal. Performed after simplifications to remove code that becomes dead 471 // as a result of those simplifications. The following optimizations could reveal more 472 // dead code which is removed right before register allocation in performRegisterAllocation. 473 DeadCodeRemover.removeDeadCode(code, codeRewriter, options); 474 assert code.isConsistentSSA(); 475 476 if (enableTryWithResourcesDesugaring()) { 477 codeRewriter.rewriteThrowableAddAndGetSuppressed(code); 478 } 479 480 if (lambdaRewriter != null) { 481 lambdaRewriter.desugarLambdas(method, code); 482 assert code.isConsistentSSA(); 483 } 484 485 if (interfaceMethodRewriter != null) { 486 interfaceMethodRewriter.rewriteMethodReferences(method, code); 487 assert code.isConsistentSSA(); 488 } 489 490 if (options.outline.enabled) { 491 outlineHandler.accept(code, method); 492 assert code.isConsistentSSA(); 493 } 494 495 codeRewriter.shortenLiveRanges(code); 496 codeRewriter.identifyReturnsArgument(method, code, feedback); 497 498 // Insert code to log arguments if requested. 499 if (options.methodMatchesLogArgumentsFilter(method)) { 500 codeRewriter.logArgumentTypes(method, code); 501 } 502 503 printMethod(code, "Optimized IR (SSA)"); 504 // Perform register allocation. 505 RegisterAllocator registerAllocator = performRegisterAllocation(code, method); 506 method.setCode(code, registerAllocator, appInfo.dexItemFactory); 507 updateHighestSortingStrings(method); 508 if (Log.ENABLED) { 509 Log.debug(getClass(), "Resulting dex code for %s:\n%s", 510 method.toSourceString(), logCode(options, method)); 511 } 512 printMethod(code, "Final IR (non-SSA)"); 513 514 // After all the optimizations have take place, we compute whether method should be inlined. 515 Constraint state; 516 if (!options.inlineAccessors || inliner == null) { 517 state = Constraint.NEVER; 518 } else { 519 state = inliner.identifySimpleMethods(code, method); 520 } 521 feedback.markProcessed(method, state); 522 } 523 updateHighestSortingStrings(DexEncodedMethod method)524 private synchronized void updateHighestSortingStrings(DexEncodedMethod method) { 525 DexString highestSortingReferencedString = method.getCode().asDexCode().highestSortingString; 526 if (highestSortingReferencedString != null) { 527 if (highestSortingString == null 528 || highestSortingReferencedString.slowCompareTo(highestSortingString) > 0) { 529 highestSortingString = highestSortingReferencedString; 530 } 531 } 532 } 533 534 // Convert a method ensuring that strings sorting equal or higher than the argument 535 // firstJumboString are encoded as jumbo strings. 536 // TODO(sgjesse): Consider replacing this with a direct dex2dex converter instead of going 537 // through IR. convertMethodJumboStringsOnly( DexEncodedMethod method, DexString firstJumboString)538 private void convertMethodJumboStringsOnly( 539 DexEncodedMethod method, DexString firstJumboString) { 540 // This is only used for methods already converted to Dex, but missing jumbo strings. 541 assert method.getCode() != null && method.getCode().isDexCode(); 542 if (options.verbose) { 543 System.out.println("Processing jumbo strings: " + method.toSourceString()); 544 } 545 if (Log.ENABLED) { 546 Log.debug(getClass(), "Original code for %s:\n%s", 547 method.toSourceString(), logCode(options, method)); 548 } 549 IRCode code = method.buildIR(options); 550 if (Log.ENABLED) { 551 Log.debug(getClass(), "Initial (SSA) flow graph for %s:\n%s", 552 method.toSourceString(), code); 553 } 554 // Compilation header if printing CFGs for this method. 555 printC1VisualizerHeader(method); 556 printMethod(code, "Initial IR (SSA)"); 557 558 // Methods passed through here should have been through IR processing already and 559 // therefore, we skip most of the IR processing. 560 561 // Perform register allocation. 562 RegisterAllocator registerAllocator = performRegisterAllocation(code, method); 563 method.setCode(code, registerAllocator, appInfo.dexItemFactory, firstJumboString); 564 565 if (Log.ENABLED) { 566 Log.debug(getClass(), "Resulting dex code for %s:\n%s", 567 method.toSourceString(), logCode(options, method)); 568 } 569 printMethod(code, "Final IR (non-SSA)"); 570 } 571 performRegisterAllocation(IRCode code, DexEncodedMethod method)572 private RegisterAllocator performRegisterAllocation(IRCode code, DexEncodedMethod method) { 573 // Always perform dead code elimination before register allocation. The register allocator 574 // does not allow dead code (to make sure that we do not waste registers for unneeded values). 575 DeadCodeRemover.removeDeadCode(code, codeRewriter, options); 576 LinearScanRegisterAllocator registerAllocator = new LinearScanRegisterAllocator(code, options); 577 registerAllocator.allocateRegisters(options.debug); 578 printMethod(code, "After register allocation (non-SSA)"); 579 printLiveRanges(registerAllocator, "Final live ranges."); 580 if (!options.debug) { 581 CodeRewriter.removedUnneededDebugPositions(code); 582 } 583 for (int i = 0; i < PEEPHOLE_OPTIMIZATION_PASSES; i++) { 584 CodeRewriter.collapsTrivialGotos(method, code); 585 PeepholeOptimizer.optimize(code, registerAllocator); 586 } 587 CodeRewriter.collapsTrivialGotos(method, code); 588 if (Log.ENABLED) { 589 Log.debug(getClass(), "Final (non-SSA) flow graph for %s:\n%s", 590 method.toSourceString(), code); 591 } 592 return registerAllocator; 593 } 594 printC1VisualizerHeader(DexEncodedMethod method)595 private void printC1VisualizerHeader(DexEncodedMethod method) { 596 if (printer != null) { 597 printer.begin("compilation"); 598 printer.print("name \"").append(method.toSourceString()).append("\"").ln(); 599 printer.print("method \"").append(method.toSourceString()).append("\"").ln(); 600 printer.print("date 0").ln(); 601 printer.end("compilation"); 602 } 603 } 604 printMethod(IRCode code, String title)605 private void printMethod(IRCode code, String title) { 606 if (printer != null) { 607 printer.resetUnusedValue(); 608 printer.begin("cfg"); 609 printer.print("name \"").append(title).append("\"\n"); 610 code.print(printer); 611 printer.end("cfg"); 612 } 613 } 614 printLiveRanges(LinearScanRegisterAllocator allocator, String title)615 private void printLiveRanges(LinearScanRegisterAllocator allocator, String title) { 616 if (printer != null) { 617 allocator.print(printer, title); 618 } 619 } 620 } 621