blob: cc582ea2ae88dc9ad49eeadeac6189be2d84e283 [file] [log] [blame]
// 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.ir.optimize.string;
import static com.android.tools.r8.ir.optimize.string.StringBuilderOptimizerAnalysisTest.checkBuilderState;
import static com.android.tools.r8.ir.optimize.string.StringBuilderOptimizerAnalysisTest.checkOptimizerStates;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ir.analysis.AnalysisTestBase;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.string.StringBuilderOptimizer.BuilderState;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.smali.SmaliBuilder;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class StringBuilderOptimizerAnalysisSmaliTest extends AnalysisTestBase {
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public StringBuilderOptimizerAnalysisSmaliTest(TestParameters parameters) throws Exception {
super(parameters, buildApp(), "TestClass");
}
private static AndroidApp buildApp() throws Exception {
SmaliBuilder smaliBuilder = new SmaliBuilder("TestClass");
{
// IR of StringConcatenationTestClass#phiAtInit at DVM older than or equal to 5.1.1
String code =
StringUtils.lines(
"invoke-static {}, Ljava/lang/System;->currentTimeMillis()J",
"move-result-wide v0",
"const-wide/16 v2, 0",
"cmp-long v4, v0, v2",
// new-instance is hoisted, but <init> calls are still at each branch
"new-instance v0, Ljava/lang/StringBuilder;",
"if-lez v4, :cond",
"const-string v1, \"Hello\"",
"invoke-direct {v0, v1}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V",
"goto :merge",
":cond",
"const-string v1, \"Hi\"",
"invoke-direct {v0, v1}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V",
"goto :merge",
":merge",
"const-string v1, \", R8\"",
"invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append"
+ "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
"sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream;",
"invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
"move-result-object v1",
"invoke-virtual {v2, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
"return-void");
smaliBuilder.addStaticMethod(
"void", "phiAtInit_5_1_1", ImmutableList.of(), 5, code);
}
{
// Compiled StringConcatenationTestClass#phiAtInit and modified two new-instance instructions
// are flown to the same <init> call.
String code =
StringUtils.lines(
"invoke-static {}, Ljava/lang/System;->currentTimeMillis()J",
"move-result-wide v0",
"const-wide/16 v2, 0",
"cmp-long v4, v0, v2",
"if-lez v4, :cond",
"new-instance v0, Ljava/lang/StringBuilder;",
"const-string v1, \"Hello\"",
"goto :merge",
":cond",
"new-instance v0, Ljava/lang/StringBuilder;",
"const-string v1, \"Hi\"",
"goto :merge",
":merge",
// Two separate new-instance instructions are flown to the same <init>.
"invoke-direct {v0, v1}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V",
"const-string v1, \", R8\"",
"invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append"
+ "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
"sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream;",
"invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
"move-result-object v1",
"invoke-virtual {v2, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
"return-void");
smaliBuilder.addStaticMethod(
"void", "phiWithDifferentNewInstance", ImmutableList.of(), 5, code);
}
{
// IR of StringConcatenationTestClass#phiAtInit at DVM/ART newer than 5.1.1
String code =
StringUtils.lines(
"invoke-static {}, Ljava/lang/System;->currentTimeMillis()J",
"move-result-wide v0",
"const-wide/16 v2, 0",
"cmp-long v4, v0, v2",
// new-instance and <init> are in each branch.
"if-lez v4, :cond",
"new-instance v0, Ljava/lang/StringBuilder;",
"const-string v1, \"Hello\"",
"invoke-direct {v0, v1}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V",
"goto :merge",
":cond",
"new-instance v0, Ljava/lang/StringBuilder;",
"const-string v1, \"Hi\"",
"invoke-direct {v0, v1}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V",
"goto :merge",
":merge",
"const-string v1, \", R8\"",
"invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append"
+ "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
"sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream;",
"invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
"move-result-object v1",
"invoke-virtual {v2, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
"return-void");
smaliBuilder.addStaticMethod(
"void", "phiAtInit", ImmutableList.of(), 5, code);
}
return AndroidApp.builder()
.addDexProgramData(smaliBuilder.compile(), Origin.unknown())
.addLibraryFile(ToolHelper.getMostRecentAndroidJar())
.build();
}
@Test
public void testPhiAtInit_5_1_1() {
buildAndCheckIR(
"phiAtInit_5_1_1",
checkOptimizerStates(appView, optimizer -> {
assertEquals(1, optimizer.analysis.builderStates.size());
for (Value builder : optimizer.analysis.builderStates.keySet()) {
Map<Instruction, BuilderState> perBuilderState =
optimizer.analysis.builderStates.get(builder);
checkBuilderState(optimizer, perBuilderState, null, true);
}
assertEquals(0, optimizer.analysis.simplifiedBuilders.size());
}));
}
@Test
public void testPhiWithDifferentNewInstance() {
buildAndCheckIR(
"phiWithDifferentNewInstance",
checkOptimizerStates(
appView,
optimizer -> {
assertEquals(0, optimizer.analysis.builderStates.size());
for (Value builder : optimizer.analysis.builderStates.keySet()) {
Map<Instruction, BuilderState> perBuilderState =
optimizer.analysis.builderStates.get(builder);
checkBuilderState(optimizer, perBuilderState, null, false);
}
assertEquals(0, optimizer.analysis.simplifiedBuilders.size());
}));
}
@Test
public void testPhiAtInit() {
buildAndCheckIR(
"phiAtInit",
checkOptimizerStates(
appView,
optimizer -> {
assertEquals(0, optimizer.analysis.builderStates.size());
for (Value builder : optimizer.analysis.builderStates.keySet()) {
Map<Instruction, BuilderState> perBuilderState =
optimizer.analysis.builderStates.get(builder);
checkBuilderState(optimizer, perBuilderState, null, false);
}
assertEquals(0, optimizer.analysis.simplifiedBuilders.size());
}));
}
}