241 lines
9.5 KiB
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);
|
|
}
|
|
}
|