blob: d42de0fd2de10c27711452b714028197c30b1c5f [file] [log] [blame]
// Copyright (c) 2016, 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.smali;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.code.Const4;
import com.android.tools.r8.code.IfEqz;
import com.android.tools.r8.code.IfGez;
import com.android.tools.r8.code.IfGtz;
import com.android.tools.r8.code.IfLez;
import com.android.tools.r8.code.IfLtz;
import com.android.tools.r8.code.IfNez;
import com.android.tools.r8.code.InvokeVirtual;
import com.android.tools.r8.code.Return;
import com.android.tools.r8.code.ReturnObject;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.ir.code.If.Type;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
public class IfSimplificationTest extends SmaliTestBase {
static String[] ifOpcode;
static {
ifOpcode = new String[6];
ifOpcode[Type.EQ.ordinal()] = "if-eq";
ifOpcode[Type.NE.ordinal()] = "if-ne";
ifOpcode[Type.LE.ordinal()] = "if-le";
ifOpcode[Type.GE.ordinal()] = "if-ge";
ifOpcode[Type.LT.ordinal()] = "if-lt";
ifOpcode[Type.GT.ordinal()] = "if-gt";
}
@Test
public void ifZeroNeqZero() {
DexEncodedMethod method = oneMethodApplication(
"int",
Collections.emptyList(),
1,
" const v0, 0",
" if-nez v0, :label_2",
":label_1",
" return v0",
":label_2",
" const v0, 1",
" goto :label_1");
DexCode code = method.getCode().asDexCode();
assertEquals(2, code.instructions.length);
assertTrue(code.instructions[0] instanceof Const4);
assertEquals(0, ((Const4) code.instructions[0]).B);
assertTrue(code.instructions[1] instanceof Return);
}
@Test
public void ifTwoEqZero() {
DexEncodedMethod method = oneMethodApplication(
"int",
Collections.emptyList(),
1,
" const v0, 2",
" if-eqz v0, :label_2",
":label_1",
" return v0",
":label_2",
" const v0, 1",
" goto :label_1");
DexCode code = method.getCode().asDexCode();
assertEquals(2, code.instructions.length);
assertTrue(code.instructions[0] instanceof Const4);
assertEquals(2, ((Const4) code.instructions[0]).B);
assertTrue(code.instructions[1] instanceof Return);
}
@Test
public void b() {
DexEncodedMethod method = oneMethodApplication(
"int",
Collections.singletonList("int"),
1,
" const v0, 0",
" if-nez v0, :label_2",
":label_1",
" return v0",
":label_2",
" if-nez p0, :label_3",
" const v0, 1",
" goto :label_1",
":label_3",
" const v0, 2",
" goto :label_1");
DexCode code = method.getCode().asDexCode();
assertEquals(2, code.instructions.length);
assertTrue(code.instructions[0] instanceof Const4);
assertEquals(0, ((Const4) code.instructions[0]).B);
assertTrue(code.instructions[1] instanceof Return);
}
@Test
public void c() {
DexEncodedMethod method = oneMethodApplication(
"int",
Collections.singletonList("int"),
1,
" const v0, 0",
" if-nez v0, :label_2",
":label_1",
" return v0",
":label_2",
" if-nez p0, :label_3",
" const v0, 1",
" goto :label_1",
":label_3",
" const p0, 0",
" goto :label_2");
DexCode code = method.getCode().asDexCode();
assertEquals(2, code.instructions.length);
assertTrue(code.instructions[0] instanceof Const4);
assertEquals(0, ((Const4) code.instructions[0]).B);
assertTrue(code.instructions[1] instanceof Return);
}
@Test
public void d() {
DexEncodedMethod method = oneMethodApplication(
"int",
Collections.singletonList("int"),
1,
" const v0, 0",
" if-nez v0, :label_2",
":label_1",
" return v0",
":label_2",
" if-nez p0, :label_3",
" const v0, 1",
" goto :label_4",
":label_3",
" const p0, 0",
" goto :label_2",
":label_4",
" if-nez p0, :label_5",
" const v0, 1",
" goto :label_4",
":label_5",
" const p0, 0",
" goto :label_2");
DexCode code = method.getCode().asDexCode();
assertEquals(2, code.instructions.length);
assertTrue(code.instructions[0] instanceof Const4);
assertEquals(0, ((Const4) code.instructions[0]).B);
assertTrue(code.instructions[1] instanceof Return);
}
@Test
public void e() {
DexEncodedMethod method = oneMethodApplication(
"int",
ImmutableList.of("int", "int", "int"),
1,
" const v0, 0",
" if-nez v0, :x",
" const v0, 1",
" if-nez p0, :x",
" const v0, 2",
" if-nez p1, :x",
" const v0, 3",
" if-nez p2, :return",
" const v0, 4",
" goto :return",
":x",
" add-int v0, v0, p0",
":return",
" return v0");
DexCode code = method.getCode().asDexCode();
assertEquals(10, code.instructions.length);
assertTrue(code.instructions[9] instanceof Return);
}
@Test
public void f() {
DexEncodedMethod method = oneMethodApplication(
"int",
Collections.singletonList("int"),
1,
" const v0, 0",
" if-nez v0, :label_2",
":label_1",
" return v0",
":label_2",
" const v0, 1",
" goto :label_2");
DexCode code = method.getCode().asDexCode();
assertEquals(2, code.instructions.length);
assertTrue(code.instructions[0] instanceof Const4);
assertEquals(0, ((Const4) code.instructions[0]).B);
assertTrue(code.instructions[1] instanceof Return);
}
@Test
public void simplifyNonZeroTests() {
class TestData {
final int a;
final int b;
final boolean results[];
TestData(int a, int b) {
this.a = a;
this.b = b;
results = new boolean[6];
results[Type.EQ.ordinal()] = a == b;
results[Type.NE.ordinal()] = a != b;
results[Type.LE.ordinal()] = a <= b;
results[Type.GE.ordinal()] = a >= b;
results[Type.LT.ordinal()] = a < b;
results[Type.GT.ordinal()] = a > b;
}
}
int[] testValues = new int[]{
100,
1,
0,
-1,
100
};
List<TestData> tests = new ArrayList<>();
for (int i = 0; i < testValues.length; i++) {
for (int j = 0; j < testValues.length; j++) {
tests.add(new TestData(testValues[i], testValues[j]));
}
}
for (TestData test : tests) {
for (Type type : Type.values()) {
DexEncodedMethod method = oneMethodApplication(
"int",
Collections.singletonList("int"),
2,
" const v0, 0x" + Integer.toHexString(test.a),
" const v1, 0x" + Integer.toHexString(test.b),
" " + ifOpcode[type.ordinal()] + " v0, v1, :label_2",
" const v0, 0",
":label_1",
" return v0",
":label_2",
" const v0, 1",
" goto :label_1");
DexCode code = method.getCode().asDexCode();
assertEquals(2, code.instructions.length);
assertTrue(code.instructions[0] instanceof Const4);
int expected = test.results[type.ordinal()] ? 1 : 0;
assertEquals(expected, ((Const4) code.instructions[0]).B);
assertTrue(code.instructions[1] instanceof Return);
}
}
}
public void runRewriteIfWithConstZeroTest(Type type, boolean zeroLeft, Class expected) {
String ifInstruction;
if (zeroLeft) {
ifInstruction = " " + ifOpcode[type.ordinal()] + " v0, v1, :label_2";
} else {
ifInstruction = " " + ifOpcode[type.ordinal()] + " v1, v0, :label_2";
}
DexEncodedMethod method = oneMethodApplication(
"int",
Collections.singletonList("int"),
1,
" const v0, 0x00",
ifInstruction,
" const v0, 0",
":label_1",
" return v0",
":label_2",
" const v0, 1",
" goto :label_1");
DexCode code = method.getCode().asDexCode();
assertEquals(5, code.instructions.length);
assertTrue(expected.isInstance(code.instructions[0]));
assertTrue(code.instructions[4] instanceof Return);
}
@Test
public void testRewriteIfWithConstZero() {
runRewriteIfWithConstZeroTest(Type.EQ, true, IfEqz.class);
runRewriteIfWithConstZeroTest(Type.NE, true, IfNez.class);
runRewriteIfWithConstZeroTest(Type.LE, true, IfGez.class);
runRewriteIfWithConstZeroTest(Type.GE, true, IfLez.class);
runRewriteIfWithConstZeroTest(Type.LT, true, IfGtz.class);
runRewriteIfWithConstZeroTest(Type.GT, true, IfLtz.class);
runRewriteIfWithConstZeroTest(Type.EQ, false, IfEqz.class);
runRewriteIfWithConstZeroTest(Type.NE, false, IfNez.class);
runRewriteIfWithConstZeroTest(Type.LE, false, IfLez.class);
runRewriteIfWithConstZeroTest(Type.GE, false, IfGez.class);
runRewriteIfWithConstZeroTest(Type.LT, false, IfLtz.class);
runRewriteIfWithConstZeroTest(Type.GT, false, IfGtz.class);
}
@Test
public void x() {
DexEncodedMethod method = oneMethodApplication(
"Test",
Lists.newArrayList("Test", "java.lang.String[]", "java.lang.String",
"java.lang.String[]", "java.lang.String"),
10,
" const/4 v4, 0x00 # 0",
" invoke-virtual { v10 }, LTest;->a()LTest;",
" if-nez v4, :label_8",
" move-object v0, v4",
" :label_7",
" return-object v0",
" :label_8",
" invoke-static { v14 }, LTest;->a([Ljava/lang/String;)LTest;",
" move-result-object v2",
" invoke-virtual { v2 }, LTest;->a()Z",
" move-result v0",
" if-nez v0, :label_20",
" move-object v0, v4",
" goto :label_7",
" :label_20",
" iget-wide v0, v2, LTest;->a:J",
" iget-wide v6, v2, LTest;->b:J",
" invoke-virtual { v2 }, LTest;->c()Z",
" move-result v2",
" if-eqz v2, :label_33",
" invoke-virtual { v4 }, LTest;->a()V",
" :label_33",
" new-instance v5, LTest;",
" sget-object v2, LTest;->a:[Ljava/lang/String;",
" invoke-direct { v5, v2 }, LTest;-><init>([Ljava/lang/String;)V",
" invoke-virtual { v10 }, LTest;->a()LTest;",
" invoke-virtual { v4, v0, v1, v6, v7 }, LTest;->a(JJ)Ljava/util/List;",
" move-result-object v2",
" invoke-interface { v2 }, Ljava/util/List;->iterator()Ljava/util/Iterator;",
" move-result-object v6",
" move-wide v2, v0",
" :label_52",
" invoke-interface { v6 }, Ljava/util/Iterator;->hasNext()Z",
" move-result v0",
" if-eqz v0, :label_107",
" invoke-interface { v6 }, Ljava/util/Iterator;->next()Ljava/lang/Object;",
" move-result-object v0",
" check-cast v0, LTest;",
" const-wide/16 v8, 0x0000000000000001 # 1",
" add-long/2addr v2, v8",
" invoke-virtual { v5 }, LTest;->newRow()LTest;",
" move-result-object v1",
" invoke-static { v2, v3 }, Ljava/lang/Long;->valueOf(J)Ljava/lang/Long;",
" move-result-object v7",
" invoke-virtual { v1, v7 }, LTest;->a(Ljava/lang/Object;)LTest;",
" move-result-object v1",
" const-string v7, \"add\"",
" invoke-virtual { v1, v7 }, LTest;->a(Ljava/lang/Object;)LTest;",
" move-result-object v1",
" iget-object v7, v0, LTest;->a:Ljava/lang/String;",
" invoke-virtual { v1, v7 }, LTest;->a(Ljava/lang/Object;)LTest;",
" move-result-object v1",
" iget v7, v0, LTest;->b:I",
" invoke-static { v7 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;",
" move-result-object v7",
" invoke-virtual { v1, v7 }, LTest;->add(Ljava/lang/Object;)LTest;",
" move-result-object v1",
" iget-object v0, v0, LTest;->a:Ljava/lang/String;",
" invoke-virtual { v1, v0 }, LTest;->add(Ljava/lang/Object;)LTest;",
" goto :label_52",
" :label_107",
" iget-object v0, v4, LTest;->a:LTest;",
" const-string v1, \"text 1\"",
" const/4 v2, 0x00 # 0",
" invoke-virtual { v0, v1, v2 }, LTest;->a(Ljava/lang/String;I)LTest;",
" move-result-object v0",
" const-string v1, \"text 2\"",
" const-string v2, \"\"",
" invoke-interface { v0, v1, v2 }, LTest;->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
" move-result-object v0",
" invoke-static { v5, v0 }, LTest;->a(LTest;Ljava/lang/String;)LTest;",
" move-result-object v0",
" goto :label_7"
);
DexCode code = method.getCode().asDexCode();
assertEquals(3, code.instructions.length);
assertTrue(code.instructions[0] instanceof InvokeVirtual);
assertTrue(code.instructions[1] instanceof Const4);
assertEquals(0, ((Const4) code.instructions[1]).B);
assertTrue(code.instructions[2] instanceof ReturnObject);
}
@Test
public void y() {
DexEncodedMethod method = oneMethodApplication(
"boolean",
Lists.newArrayList("Test", "java.lang.Object"),
6,
" const-wide/16 v4, 0x0000000000000000L # 0",
" const/4 v0, 0x01 # 1",
" const/4 v3, 0x00 # 0",
" const/4 v1, 0x00 # 0",
" if-ne v6, v7, :label_8",
" :label_7",
" return v0",
" :label_8",
" if-nez v7, :label_12",
" move v0, v1",
" goto :label_7",
" :label_12",
" instance-of v2, v7, LTest;",
" if-nez v2, :label_18",
" move v0, v1",
" goto :label_7",
" :label_18",
" check-cast v7, LTest;",
" cmp-long v2, v4, v4",
" if-nez v2, :label_50",
" invoke-static { v3, v3 }, LTest;->a(Ljava/lang/Object;Ljava/lang/Object;)Z",
" move-result v2",
" if-eqz v2, :label_50",
" invoke-static { v3, v3 }, LTest;->a(Ljava/lang/Object;Ljava/lang/Object;)Z",
" move-result v2",
" if-eqz v2, :label_50",
" invoke-static { v1 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;",
" move-result-object v2",
" invoke-static { v1 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;",
" move-result-object v3",
" invoke-static { v2, v3 }, LTest;->a(Ljava/lang/Object;Ljava/lang/Object;)Z",
" move-result v2",
" if-nez v2, :label_7",
" :label_50",
" move v0, v1",
" goto :label_7"
);
DexCode code = method.getCode().asDexCode();
// TODO(sgjesse): Maybe this test is too fragile, as it leaves quite a lot of code, so the
// expectation might need changing with other optimizations.
// TODO(zerny): Consider optimizing the fallthrough branch of conditionals to not be return.
assertEquals(24, code.instructions.length);
}
}