blob: 88785c4f6b741f5544ffe6a70b180575d7d3e9e8 [file] [log] [blame]
// 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.outValue().getTypeLattice().isReference());
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();
}
}