|  | // Copyright (c) 2018, 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.ir; | 
|  |  | 
|  | import static org.junit.Assert.assertTrue; | 
|  |  | 
|  | import com.android.tools.r8.TestBase; | 
|  | 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.ir.code.LinearFlowInstructionListIterator; | 
|  | import com.android.tools.r8.jasmin.JasminBuilder; | 
|  | import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder; | 
|  | import com.android.tools.r8.utils.AndroidApp; | 
|  | import com.android.tools.r8.utils.codeinspector.MethodSubject; | 
|  | import com.google.common.collect.ImmutableList; | 
|  | import java.util.ListIterator; | 
|  | import org.junit.Test; | 
|  |  | 
|  | public class LinearFlowIteratorTest extends TestBase { | 
|  |  | 
|  | private IRCode branchingCode() throws Exception { | 
|  |  | 
|  | JasminBuilder jasminBuilder = new JasminBuilder(); | 
|  |  | 
|  | ClassBuilder clazz = jasminBuilder.addClass("foo"); | 
|  | clazz.addStaticMethod( | 
|  | "bar", | 
|  | ImmutableList.of("I"), | 
|  | "V", | 
|  | ".limit stack 2", | 
|  | ".limit locals 2", | 
|  | ".var 1 is x Ljava/lang/Object; from L1 to L2", | 
|  | "  aconst_null", | 
|  | "  astore 1", | 
|  | "L1:", | 
|  | "  iload 0", | 
|  | "  ifeq L3", | 
|  | "L2:", | 
|  | "  goto L5", | 
|  | "L3:", | 
|  | "  aload 1", | 
|  | "  iconst_0", | 
|  | "  aaload", | 
|  | "  pop", | 
|  | "L5:", | 
|  | "  return"); | 
|  | AndroidApp.Builder appBuilder = AndroidApp.builder(); | 
|  | appBuilder.addClassProgramData(jasminBuilder.buildClasses()); | 
|  |  | 
|  | // Build the code, and split the code into three blocks. | 
|  | AndroidApp app = compileWithD8(appBuilder.build()); | 
|  | MethodSubject methodSubject = | 
|  | getMethodSubject(app, "foo", "void", "bar", ImmutableList.of("int")); | 
|  | IRCode code = methodSubject.buildIR(); | 
|  | ListIterator<BasicBlock> blocks = code.listIterator(); | 
|  | blocks.next(); | 
|  | InstructionListIterator iter = blocks.next().listIterator(code); | 
|  | iter.nextUntil(i -> !i.isConstNumber()); | 
|  | iter.previous(); | 
|  | iter.split(code, blocks); | 
|  | return code; | 
|  | } | 
|  |  | 
|  | private IRCode simpleCode() throws Exception { | 
|  |  | 
|  | JasminBuilder jasminBuilder = new JasminBuilder(); | 
|  |  | 
|  | ClassBuilder clazz = jasminBuilder.addClass("foo"); | 
|  | clazz.addStaticMethod( | 
|  | "bar", | 
|  | ImmutableList.of("I"), | 
|  | "V", | 
|  | ".limit stack 2", | 
|  | ".limit locals 2", | 
|  | ".var 0 is x I from L1 to L2", | 
|  | "L1:", | 
|  | "  aconst_null", | 
|  | "  iload 0", | 
|  | "  aaload", | 
|  | "  pop", | 
|  | "L2:", | 
|  | "  return"); | 
|  | AndroidApp.Builder appBuilder = AndroidApp.builder(); | 
|  | appBuilder.addClassProgramData(jasminBuilder.buildClasses()); | 
|  |  | 
|  | // Build the code, and split the code into three blocks. | 
|  | AndroidApp app = compileWithD8(appBuilder.build()); | 
|  | MethodSubject method = getMethodSubject(app, "foo", "void", "bar", ImmutableList.of("int")); | 
|  | IRCode code = method.buildIR(); | 
|  | ListIterator<BasicBlock> blocks = code.listIterator(); | 
|  | InstructionListIterator iter = blocks.next().listIterator(code); | 
|  | iter.nextUntil(i -> !i.isArgument()); | 
|  | iter.split(code, 0, blocks); | 
|  | return code; | 
|  | } | 
|  |  | 
|  | @Test | 
|  | public void hasNextWillCheckNextBlock() throws Exception { | 
|  | IRCode code = simpleCode(); | 
|  | InstructionListIterator it = new LinearFlowInstructionListIterator(code, code.entryBlock()); | 
|  | it.next(); | 
|  | it.next(); | 
|  | assert it.hasNext(); | 
|  | } | 
|  |  | 
|  | @Test | 
|  | public void nextWillContinueThroughGotoBlocks() throws Exception { | 
|  | IRCode code = simpleCode(); | 
|  | InstructionListIterator it = new LinearFlowInstructionListIterator(code, code.entryBlock()); | 
|  | it.next(); // Argument | 
|  | it.next(); // ConstNumber 0/NULL | 
|  | it.next(); // ArrayGet | 
|  | assert it.next().isReturn(); // Return | 
|  | } | 
|  |  | 
|  | @Test | 
|  | public void hasPreviousWillCheckPreviousBlock() throws Exception { | 
|  | IRCode code = simpleCode(); | 
|  | InstructionListIterator it = new LinearFlowInstructionListIterator(code, code.blocks.get(2)); | 
|  | assert it.hasPrevious(); | 
|  | } | 
|  |  | 
|  | @Test | 
|  | public void hasPreviousWillJumpOverGotos() throws Exception { | 
|  | IRCode code = simpleCode(); | 
|  | InstructionListIterator it = new LinearFlowInstructionListIterator(code, code.blocks.get(2)); | 
|  | assert it.previous().isConstNumber(); | 
|  | } | 
|  |  | 
|  | @Test | 
|  | public void GoToFrontAndBackIsSameAmountOfInstructions() throws Exception { | 
|  | IRCode code = simpleCode(); | 
|  | int moves = 0; | 
|  | InstructionListIterator it = new LinearFlowInstructionListIterator(code, code.entryBlock()); | 
|  | while (it.hasNext()) { | 
|  | it.next(); | 
|  | moves++; | 
|  | } | 
|  | Instruction current = null; | 
|  | for (int i = 0; i < moves; i++) { | 
|  | current = it.previous(); | 
|  | } | 
|  | assert !it.hasPrevious(); | 
|  | assert current.isArgument(); | 
|  | } | 
|  |  | 
|  | @Test | 
|  | public void moveFromEmptyBlock() throws Exception { | 
|  | IRCode code = simpleCode(); | 
|  | InstructionListIterator it = new LinearFlowInstructionListIterator(code, code.blocks.get(1)); | 
|  | Instruction current = it.previous(); | 
|  | assertTrue(current.isConstNumber() && current.getOutType().isReferenceType()); | 
|  | it.next(); | 
|  | current = it.next(); | 
|  | assertTrue(current.isArrayGet()); | 
|  | } | 
|  |  | 
|  | @Test | 
|  | public void doNotChangeToNextBlockWhenNotLinearFlow() throws Exception { | 
|  | IRCode code = branchingCode(); | 
|  | InstructionListIterator it = new LinearFlowInstructionListIterator(code, code.entryBlock()); | 
|  | it.nextUntil((i) -> !i.isArgument()); | 
|  | it.next(); | 
|  | assert !it.hasNext(); | 
|  | } | 
|  |  | 
|  | @Test | 
|  | public void doNotChangeToPreviousBlockWhenNotLinearFlow() throws Exception { | 
|  | IRCode code = branchingCode(); | 
|  | InstructionListIterator it = new LinearFlowInstructionListIterator(code, code.blocks.get(4)); | 
|  | assert !it.hasPrevious(); | 
|  | } | 
|  |  | 
|  | @Test | 
|  | public void followLinearSubPathDown() throws Exception { | 
|  | IRCode code = branchingCode(); | 
|  | InstructionListIterator it = new LinearFlowInstructionListIterator(code, code.blocks.get(1)); | 
|  | Instruction current = null; | 
|  | while (it.hasNext()) { | 
|  | current = it.next(); | 
|  | } | 
|  | assert current.isGoto(); | 
|  | } | 
|  |  | 
|  | @Test | 
|  | public void followLinearSubPathUp() throws Exception { | 
|  | IRCode code = branchingCode(); | 
|  | InstructionListIterator it = new LinearFlowInstructionListIterator(code, code.blocks.get(2)); | 
|  | Instruction current = null; | 
|  | while (it.hasPrevious()) { | 
|  | current = it.previous(); | 
|  | } | 
|  | assert current.isConstNumber(); | 
|  | } | 
|  | } |