java-topology/defects/gcc/unit/GccGimpleRangePathTest.java

241 lines
9.5 KiB
Java

package unit;
import java.util.*;
/**
* Unit test for gcc-0002: path_range_query::compute_exit_dependencies O(P^2*N) defect.
*
* Models the pattern in gcc/gimple-range-path.cc:
*
* while (!worklist.is_empty())
* {
* name = worklist.pop();
* def_bb = def_stmt(name).bb;
* if (!m_path.contains(def_bb)) // O(P) linear scan — the defect
* continue;
* for (phi args e) {
* if (m_path.contains(e.src) // O(P) linear scan — the defect
* && new_dep(arg))
* worklist.push(arg);
* }
* }
*
* m_path is an auto_vec<basic_block>, .contains() does a linear walk.
*
* Slow path: List.contains() — O(P) per worklist item.
* Fast path: pre-built HashSet from path — O(1) per worklist item.
*/
public class GccGimpleRangePathTest {
/**
* Simulate compute_exit_dependencies.
*
* @param pathBBs the set of basic-block IDs on the path (m_path)
* @param phiGraph phiGraph[defBB] = list of {argBB, argId} phi arg pairs
* @param initDeps initial SSA names in the worklist (imported by exit BB)
* @param defBlock defBlock[ssaName] = the BB where it is defined
* @param useList list, not set — mirrors auto_vec<basic_block>
* @return op count (each contains() call = 1 op + |path| for list)
*/
static long[] computeDepsOps(
List<Integer> pathList, // m_path as list
Set<Integer> pathSet, // fast version: null for slow
Map<Integer, List<int[]>> phiGraph, // defBB → [[srcBB, argSSA], ...]
Set<Integer> initDeps,
Map<Integer, Integer> defBlock) {
Set<Integer> dependencies = new HashSet<>(initDeps);
Deque<Integer> worklist = new ArrayDeque<>(initDeps);
long ops = 0;
while (!worklist.isEmpty()) {
int name = worklist.pop();
Integer defBB = defBlock.get(name);
if (defBB == null) continue;
// contains check — O(P) slow, O(1) fast
boolean onPath;
if (pathSet != null) {
ops++; // O(1)
onPath = pathSet.contains(defBB);
} else {
ops++;
int found = 0;
for (int bb : pathList) { ops++; if (bb == defBB) { found = 1; break; } }
onPath = found == 1;
}
if (!onPath) continue;
List<int[]> phiArgs = phiGraph.get(defBB);
if (phiArgs == null) continue;
for (int[] arg : phiArgs) {
int srcBB = arg[0], argSSA = arg[1];
// src contains check
boolean srcOnPath;
if (pathSet != null) {
ops++;
srcOnPath = pathSet.contains(srcBB);
} else {
ops++;
int found = 0;
for (int bb : pathList) { ops++; if (bb == srcBB) { found = 1; break; } }
srcOnPath = found == 1;
}
if (srcOnPath && dependencies.add(argSSA))
worklist.push(argSSA);
}
}
return new long[]{ ops, dependencies.size() };
}
static long slowOps(List<Integer> path, Map<Integer,List<int[]>> phi,
Set<Integer> initDeps, Map<Integer,Integer> defBlock) {
return computeDepsOps(path, null, phi, initDeps, defBlock)[0];
}
static long[] fastResult(List<Integer> path, Map<Integer,List<int[]>> phi,
Set<Integer> initDeps, Map<Integer,Integer> defBlock) {
Set<Integer> pathSet = new HashSet<>(path);
return computeDepsOps(path, pathSet, phi, initDeps, defBlock);
}
static long slowResult(List<Integer> path, Map<Integer,List<int[]>> phi,
Set<Integer> initDeps, Map<Integer,Integer> defBlock) {
return computeDepsOps(path, null, phi, initDeps, defBlock)[1];
}
// Build a chain: path = [0,1,...,P-1], each BB i defines SSA i,
// phi at BB i has one arg from BB i-1 with SSA (i + P).
// init deps = {0} (SSA name 0 defined in BB 0 on path)
static Object[] buildChain(int P) {
List<Integer> path = new ArrayList<>();
for (int i = 0; i < P; i++) path.add(i);
Map<Integer, List<int[]>> phi = new HashMap<>();
Map<Integer, Integer> defBlock = new HashMap<>();
// SSA name i defined in BB i
for (int i = 0; i < P; i++) {
defBlock.put(i, i);
if (i > 0) {
// BB i has a phi with arg from BB (i-1) using SSA (i-1)
phi.computeIfAbsent(i, k -> new ArrayList<>())
.add(new int[]{ i - 1, i - 1 });
}
}
Set<Integer> initDeps = new HashSet<>();
initDeps.add(P - 1); // start from the last SSA name
return new Object[]{ path, phi, initDeps, defBlock };
}
@SuppressWarnings("unchecked")
public static void main(String[] args) {
int pass = 0, total = 0;
// Test 1: correctness - small path, both paths find same dependencies
total++;
{
Object[] c = buildChain(5);
List<Integer> path = (List<Integer>) c[0];
Map<Integer,List<int[]>> phi = (Map<Integer,List<int[]>>) c[1];
Set<Integer> initDeps = (Set<Integer>) c[2];
Map<Integer,Integer> defBlock = (Map<Integer,Integer>) c[3];
long slowDeps = slowResult(path, phi, initDeps, defBlock);
long[] fastR = fastResult(path, phi, initDeps, defBlock);
if (slowDeps == fastR[1]) {
pass++;
System.out.printf("PASS test1: correctness — both find %d dependencies%n", slowDeps);
} else {
System.out.printf("FAIL test1: slow=%d fast=%d%n", slowDeps, fastR[1]);
}
}
// Test 2: op count slow >> fast for large P
total++;
{
int P = 100;
Object[] c = buildChain(P);
List<Integer> path = (List<Integer>) c[0];
Map<Integer,List<int[]>> phi = (Map<Integer,List<int[]>>) c[1];
Set<Integer> initDeps = (Set<Integer>) c[2];
Map<Integer,Integer> defBlock = (Map<Integer,Integer>) c[3];
long slow = slowOps(path, phi, initDeps, defBlock);
long fast = fastResult(path, phi, initDeps, defBlock)[0];
boolean slowBig = slow > P * 2;
boolean fastSmall = fast < slow / 2;
if (slowBig && fastSmall) {
pass++;
System.out.printf("PASS test2: P=%d slow=%d ops fast=%d ops%n", P, slow, fast);
} else {
System.out.printf("FAIL test2: P=%d slow=%d fast=%d (expected slow>>fast)%n",
P, slow, fast);
}
}
// Test 3: speedup >= 5x at P=50
total++;
{
int P = 50;
Object[] c = buildChain(P);
List<Integer> path = (List<Integer>) c[0];
Map<Integer,List<int[]>> phi = (Map<Integer,List<int[]>>) c[1];
Set<Integer> initDeps = (Set<Integer>) c[2];
Map<Integer,Integer> defBlock = (Map<Integer,Integer>) c[3];
long slow = slowOps(path, phi, initDeps, defBlock);
long fast = fastResult(path, phi, initDeps, defBlock)[0];
long ratio = slow / Math.max(fast, 1);
if (ratio >= 5) {
pass++;
System.out.printf("PASS test3: speedup %dx at P=%d%n", ratio, P);
} else {
System.out.printf("FAIL test3: speedup only %dx at P=%d (slow=%d fast=%d)%n",
ratio, P, slow, fast);
}
}
// Test 4: empty path — no dependencies found
total++;
{
List<Integer> path = new ArrayList<>();
Map<Integer,List<int[]>> phi = new HashMap<>();
Set<Integer> initDeps = new HashSet<>(Arrays.asList(0, 1, 2));
Map<Integer,Integer> defBlock = new HashMap<>();
defBlock.put(0, 99); defBlock.put(1, 98); defBlock.put(2, 97);
long slowDeps = slowResult(path, phi, initDeps, defBlock);
long[] fastR = fastResult(path, phi, initDeps, defBlock);
// none should be found (none of defBlocks are on path)
if (slowDeps == fastR[1]) {
pass++;
System.out.printf("PASS test4: empty path — both find 0 deps on path (slow=%d fast=%d)%n",
slowDeps, fastR[1]);
} else {
System.out.printf("FAIL test4: slow=%d fast=%d%n", slowDeps, fastR[1]);
}
}
// Test 5: single BB path
total++;
{
List<Integer> path = new ArrayList<>(Collections.singletonList(0));
Map<Integer,List<int[]>> phi = new HashMap<>();
Set<Integer> initDeps = new HashSet<>(Collections.singletonList(0));
Map<Integer,Integer> defBlock = new HashMap<>();
defBlock.put(0, 0);
long slowDeps = slowResult(path, phi, initDeps, defBlock);
long[] fastR = fastResult(path, phi, initDeps, defBlock);
if (slowDeps == fastR[1]) {
pass++;
System.out.printf("PASS test5: single BB — both find %d deps%n", slowDeps);
} else {
System.out.printf("FAIL test5: slow=%d fast=%d%n", slowDeps, fastR[1]);
}
}
System.out.printf("%n%d/%d PASS%n", pass, total);
if (pass != total) System.exit(1);
}
}