| // Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| package com.android.tools.r8.cf; | |
| import static org.junit.Assert.assertTrue; | |
| import com.android.tools.r8.CompilationMode; | |
| import com.android.tools.r8.TestBase; | |
| import com.android.tools.r8.cf.code.CfInstruction; | |
| import com.android.tools.r8.cf.code.CfInvoke; | |
| import com.android.tools.r8.cf.code.CfLabel; | |
| import com.android.tools.r8.cf.code.CfLoad; | |
| import com.android.tools.r8.cf.code.CfStackInstruction; | |
| import com.android.tools.r8.graph.CfCode; | |
| import com.android.tools.r8.ir.code.BasicBlock; | |
| import com.android.tools.r8.ir.code.IRCode; | |
| import com.android.tools.r8.ir.code.Instruction; | |
| import com.android.tools.r8.ir.code.InstructionListIterator; | |
| import com.android.tools.r8.utils.StringUtils; | |
| import com.android.tools.r8.utils.codeinspector.ClassSubject; | |
| import com.android.tools.r8.utils.codeinspector.CodeInspector; | |
| import java.util.List; | |
| import org.junit.Test; | |
| /** | |
| * This tests that we produce valid code when having normal-flow with exceptional edges in blocks. | |
| * We might perform optimizations that add operations (dup, swap, etc.) before and after | |
| * instructions that lie on the boundary of the exception table that is generated for a basic block. | |
| * If live-ranges are minimized this could produce VerifyErrors. | |
| */ | |
| public class TryRangeTestRunner extends TestBase { | |
| @Test | |
| public void testRegisterAllocationLimitTrailingRange() throws Exception { | |
| testForR8(Backend.CF) | |
| .addProgramClasses(TryRangeTest.class) | |
| .addKeepMainRule(TryRangeTest.class) | |
| .setMode(CompilationMode.RELEASE) | |
| .noMinification() | |
| .noTreeShaking() | |
| .enableInliningAnnotations() | |
| .addOptionsModification(o -> o.enableLoadStoreOptimization = false) | |
| .run(TryRangeTest.class) | |
| .assertSuccessWithOutput(StringUtils.lines("10", "7.0")); | |
| } | |
| @Test | |
| public void testRegisterAllocationLimitLeadingRange() throws Exception { | |
| CodeInspector inspector = | |
| testForR8(Backend.CF) | |
| .addProgramClasses(TryRangeTestLimitRange.class) | |
| .addKeepMainRule(TryRangeTestLimitRange.class) | |
| .setMode(CompilationMode.RELEASE) | |
| .noMinification() | |
| .noTreeShaking() | |
| .enableInliningAnnotations() | |
| .addOptionsModification( | |
| o -> { | |
| o.enableLoadStoreOptimization = false; | |
| o.testing.irModifier = this::processIR; | |
| }) | |
| .run(TryRangeTestLimitRange.class) | |
| .assertSuccessWithOutput("") | |
| .inspector(); | |
| // Assert that we do not have any register-modifying instructions in the throwing block: | |
| // L0: ; locals: | |
| // iload 1; | |
| // invokestatic com.android.tools.r8.cf.TryRangeTestLimitRange.doSomething(I)F | |
| // L1: ; locals: | |
| // 11: pop | |
| ClassSubject clazz = inspector.clazz("com.android.tools.r8.cf.TryRangeTestLimitRange"); | |
| CfCode cfCode = clazz.uniqueMethodWithName("main").getMethod().getCode().asCfCode(); | |
| List<CfInstruction> instructions = cfCode.getInstructions(); | |
| CfLabel startLabel = cfCode.getTryCatchRanges().get(0).start; | |
| int index = 0; | |
| while (instructions.get(index) != startLabel) { | |
| index++; | |
| } | |
| assert instructions.get(index + 1) instanceof CfLoad; | |
| assert instructions.get(index + 2) instanceof CfInvoke; | |
| assert instructions.get(index + 3) == cfCode.getTryCatchRanges().get(0).end; | |
| assert instructions.get(index + 4) instanceof CfStackInstruction; | |
| } | |
| private void processIR(IRCode code) { | |
| if (!code.method().qualifiedName().equals(TryRangeTestLimitRange.class.getName() + ".main")) { | |
| return; | |
| } | |
| BasicBlock entryBlock = code.entryBlock(); | |
| BasicBlock tryBlock = code.blocks.get(1); | |
| assertTrue(tryBlock.hasCatchHandlers()); | |
| InstructionListIterator it = entryBlock.listIterator(code); | |
| Instruction constNumber = it.next(); | |
| while (!constNumber.isConstNumber()) { | |
| constNumber = it.next(); | |
| } | |
| it.removeInstructionIgnoreOutValue(); | |
| Instruction add = it.next(); | |
| while (!add.isAdd()) { | |
| add = it.next(); | |
| } | |
| it.removeInstructionIgnoreOutValue(); | |
| constNumber.setBlock(tryBlock); | |
| add.setBlock(tryBlock); | |
| tryBlock.getInstructions().add(0, add); | |
| tryBlock.getInstructions().add(0, constNumber); | |
| } | |
| } |