| // 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()); |
| assertTrue(it.next().isArgument()); |
| assertTrue(it.next().isConstNumber()); |
| assertTrue(it.next().isThrow()); |
| } |
| |
| @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()); |
| assertTrue(it.next().isConstNumber()); |
| assertTrue(it.next().isThrow()); |
| } |
| |
| @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(); |
| } |
| } |