blob: 2d1f0e55f902475fc68ace7df94eaa00b3e1cb87 [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.conversion;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.ir.conversion.CallGraph.Node;
import com.android.tools.r8.ir.conversion.CallGraphBuilderBase.CycleEliminator;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Sets;
import java.util.Set;
import java.util.TreeSet;
import org.junit.Test;
public class NodeExtractionTest extends CallGraphTestBase {
private InternalOptions options = new InternalOptions();
// Note that building a test graph is intentionally repeated to avoid race conditions and/or
// non-deterministic test results due to cycle elimination.
@Test
public void testExtractLeaves_withoutCycle() {
Node n1, n2, n3, n4, n5, n6;
Set<Node> nodes;
n1 = createNode("n1");
n2 = createNode("n2");
n3 = createNode("n3");
n4 = createNode("n4");
n5 = createNode("n5");
n6 = createNode("n6");
n2.addCallerConcurrently(n1);
n3.addCallerConcurrently(n2);
n4.addCallerConcurrently(n2);
n4.addCallerConcurrently(n5);
n6.addCallerConcurrently(n5);
nodes = new TreeSet<>();
nodes.add(n1);
nodes.add(n2);
nodes.add(n3);
nodes.add(n4);
nodes.add(n5);
nodes.add(n6);
Set<Node> wave = Sets.newIdentityHashSet();
PrimaryMethodProcessor.extractLeaves(nodes, wave::add);
assertEquals(3, wave.size());
assertThat(wave, hasItem(n3));
assertThat(wave, hasItem(n4));
assertThat(wave, hasItem(n6));
wave.clear();
PrimaryMethodProcessor.extractLeaves(nodes, wave::add);
assertEquals(2, wave.size());
assertThat(wave, hasItem(n2));
assertThat(wave, hasItem(n5));
wave.clear();
PrimaryMethodProcessor.extractLeaves(nodes, wave::add);
assertEquals(1, wave.size());
assertThat(wave, hasItem(n1));
assertTrue(nodes.isEmpty());
}
@Test
public void testExtractLeaves_withCycle() {
Node n1, n2, n3, n4, n5, n6;
Set<Node> nodes;
n1 = createNode("n1");
n2 = createNode("n2");
n3 = createNode("n3");
n4 = createNode("n4");
n5 = createNode("n5");
n6 = createNode("n6");
n2.addCallerConcurrently(n1);
n3.addCallerConcurrently(n2);
n4.addCallerConcurrently(n2);
n4.addCallerConcurrently(n5);
n6.addCallerConcurrently(n5);
nodes = new TreeSet<>();
nodes.add(n1);
nodes.add(n2);
nodes.add(n3);
nodes.add(n4);
nodes.add(n5);
nodes.add(n6);
n1.addCallerConcurrently(n3);
n3.method.getMutableOptimizationInfo().markForceInline();
CycleEliminator cycleEliminator = new CycleEliminator(nodes, options);
assertEquals(1, cycleEliminator.breakCycles().numberOfRemovedEdges());
Set<Node> wave = Sets.newIdentityHashSet();
PrimaryMethodProcessor.extractLeaves(nodes, wave::add);
assertEquals(3, wave.size());
assertThat(wave, hasItem(n3));
assertThat(wave, hasItem(n4));
assertThat(wave, hasItem(n6));
wave.clear();
PrimaryMethodProcessor.extractLeaves(nodes, wave::add);
assertEquals(2, wave.size());
assertThat(wave, hasItem(n2));
assertThat(wave, hasItem(n5));
wave.clear();
PrimaryMethodProcessor.extractLeaves(nodes, wave::add);
assertEquals(1, wave.size());
assertThat(wave, hasItem(n1));
assertTrue(nodes.isEmpty());
}
@Test
public void testExtractRoots_withoutCycle() {
Node n1, n2, n3, n4, n5, n6;
Set<Node> nodes;
n1 = createNode("n1");
n2 = createNode("n2");
n3 = createNode("n3");
n4 = createNode("n4");
n5 = createNode("n5");
n6 = createNode("n6");
n2.addCallerConcurrently(n1);
n3.addCallerConcurrently(n2);
n4.addCallerConcurrently(n2);
n4.addCallerConcurrently(n5);
n6.addCallerConcurrently(n5);
nodes = new TreeSet<>();
nodes.add(n1);
nodes.add(n2);
nodes.add(n3);
nodes.add(n4);
nodes.add(n5);
nodes.add(n6);
CallGraph callGraph = new CallGraph(nodes, null);
Set<DexEncodedMethod> wave = Sets.newIdentityHashSet();
wave.addAll(callGraph.extractRoots());
assertEquals(2, wave.size());
assertThat(wave, hasItem(n1.method));
assertThat(wave, hasItem(n5.method));
wave.clear();
wave.addAll(callGraph.extractRoots());
assertEquals(2, wave.size());
assertThat(wave, hasItem(n2.method));
assertThat(wave, hasItem(n6.method));
wave.clear();
wave.addAll(callGraph.extractRoots());
assertEquals(2, wave.size());
assertThat(wave, hasItem(n3.method));
assertThat(wave, hasItem(n4.method));
assertTrue(nodes.isEmpty());
}
@Test
public void testExtractRoots_withCycle() {
Node n1, n2, n3, n4, n5, n6;
Set<Node> nodes;
n1 = createNode("n1");
n2 = createNode("n2");
n3 = createNode("n3");
n4 = createNode("n4");
n5 = createNode("n5");
n6 = createNode("n6");
n2.addCallerConcurrently(n1);
n3.addCallerConcurrently(n2);
n4.addCallerConcurrently(n2);
n4.addCallerConcurrently(n5);
n6.addCallerConcurrently(n5);
nodes = new TreeSet<>();
nodes.add(n1);
nodes.add(n2);
nodes.add(n3);
nodes.add(n4);
nodes.add(n5);
nodes.add(n6);
n1.addCallerConcurrently(n3);
n3.method.getMutableOptimizationInfo().markForceInline();
CycleEliminator cycleEliminator = new CycleEliminator(nodes, options);
assertEquals(1, cycleEliminator.breakCycles().numberOfRemovedEdges());
CallGraph callGraph = new CallGraph(nodes, null);
Set<DexEncodedMethod> wave = Sets.newIdentityHashSet();
wave.addAll(callGraph.extractRoots());
assertEquals(2, wave.size());
assertThat(wave, hasItem(n1.method));
assertThat(wave, hasItem(n5.method));
wave.clear();
wave.addAll(callGraph.extractRoots());
assertEquals(2, wave.size());
assertThat(wave, hasItem(n2.method));
assertThat(wave, hasItem(n6.method));
wave.clear();
wave.addAll(callGraph.extractRoots());
assertEquals(2, wave.size());
assertThat(wave, hasItem(n3.method));
assertThat(wave, hasItem(n4.method));
assertTrue(nodes.isEmpty());
}
}