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, .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 * @return op count (each contains() call = 1 op + |path| for list) */ static long[] computeDepsOps( List pathList, // m_path as list Set pathSet, // fast version: null for slow Map> phiGraph, // defBB → [[srcBB, argSSA], ...] Set initDeps, Map defBlock) { Set dependencies = new HashSet<>(initDeps); Deque 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 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 path, Map> phi, Set initDeps, Map defBlock) { return computeDepsOps(path, null, phi, initDeps, defBlock)[0]; } static long[] fastResult(List path, Map> phi, Set initDeps, Map defBlock) { Set pathSet = new HashSet<>(path); return computeDepsOps(path, pathSet, phi, initDeps, defBlock); } static long slowResult(List path, Map> phi, Set initDeps, Map 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 path = new ArrayList<>(); for (int i = 0; i < P; i++) path.add(i); Map> phi = new HashMap<>(); Map 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 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 path = (List) c[0]; Map> phi = (Map>) c[1]; Set initDeps = (Set) c[2]; Map defBlock = (Map) 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 path = (List) c[0]; Map> phi = (Map>) c[1]; Set initDeps = (Set) c[2]; Map defBlock = (Map) 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 path = (List) c[0]; Map> phi = (Map>) c[1]; Set initDeps = (Set) c[2]; Map defBlock = (Map) 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 path = new ArrayList<>(); Map> phi = new HashMap<>(); Set initDeps = new HashSet<>(Arrays.asList(0, 1, 2)); Map 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 path = new ArrayList<>(Collections.singletonList(0)); Map> phi = new HashMap<>(); Set initDeps = new HashSet<>(Collections.singletonList(0)); Map 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); } }