package unit; import java.util.*; /** * Unit test for llvm-0004: DomConditionCache::registerBranch O(B^2) defect. * * Models the DomConditionCache pattern: * AffectedValues: Map> * registerBranch(branch): * for each affected Value V: * if !AV[V].contains(branch): // O(|AV[V]|) — the defect * AV[V].add(branch) * * Slow path: is_contained() = List.contains() — O(B) per branch per value * Fast path: pre-built HashSet — O(1) per branch per value */ public class LlvmDomConditionCacheTest { // ---- slow path: mirrors the defective LLVM code ---- static long slowRegisterBranches(int numBranches, int numValuesPerBranch) { // AffectedValues: for each Value index, a list of branch indices List> affectedValues = new ArrayList<>(); for (int v = 0; v < numValuesPerBranch; v++) affectedValues.add(new ArrayList<>()); long ops = 0; for (int b = 0; b < numBranches; b++) { // Each branch affects numValuesPerBranch shared values for (int v = 0; v < numValuesPerBranch; v++) { List av = affectedValues.get(v); // O(|av|) scan — the defect ops++; boolean found = false; for (int existing : av) { // linear scan ops++; if (existing == b) { found = true; break; } } if (!found) av.add(b); } } return ops; } // ---- fast path: pre-built set for O(1) membership ---- static long fastRegisterBranches(int numBranches, int numValuesPerBranch) { List> affectedSets = new ArrayList<>(); for (int v = 0; v < numValuesPerBranch; v++) affectedSets.add(new HashSet<>()); long ops = 0; for (int b = 0; b < numBranches; b++) { for (int v = 0; v < numValuesPerBranch; v++) { Set av = affectedSets.get(v); ops++; // O(1) hash lookup av.add(b); } } return ops; } // ---- correctness: both paths produce same AV lists ---- static boolean correctnessCheck(int branches, int valuesPerBranch) { List> slowAV = new ArrayList<>(); for (int v = 0; v < valuesPerBranch; v++) slowAV.add(new ArrayList<>()); for (int b = 0; b < branches; b++) for (int v = 0; v < valuesPerBranch; v++) { List av = slowAV.get(v); if (!av.contains(b)) av.add(b); } List> fastAV = new ArrayList<>(); for (int v = 0; v < valuesPerBranch; v++) fastAV.add(new HashSet<>()); for (int b = 0; b < branches; b++) for (int v = 0; v < valuesPerBranch; v++) fastAV.get(v).add(b); for (int v = 0; v < valuesPerBranch; v++) { Set slowSet = new HashSet<>(slowAV.get(v)); if (!slowSet.equals(fastAV.get(v))) return false; } return true; } public static void main(String[] args) { int pass = 0, total = 0; // Test 1: correctness small total++; if (correctnessCheck(10, 3)) { pass++; System.out.println("PASS test1: correctness (10 branches, 3 values)"); } else System.out.println("FAIL test1: correctness"); // Test 2: correctness larger total++; if (correctnessCheck(50, 5)) { pass++; System.out.println("PASS test2: correctness (50 branches, 5 values)"); } else System.out.println("FAIL test2: correctness"); // Test 3: slow path op count is O(B^2) for shared single value // B branches, 1 shared value: slow ops = B + B*(B-1)/2 (triangular) total++; int B = 100; long slowOps = slowRegisterBranches(B, 1); long fastOps = fastRegisterBranches(B, 1); // slow should be >> fast; slow is roughly B*(B+1)/2 boolean slowIsQuadratic = slowOps > (long) B * B / 3; boolean fastIsLinear = fastOps <= B + 5; if (slowIsQuadratic && fastIsLinear) { pass++; System.out.printf("PASS test3: op count slow=%d O(B^2) fast=%d O(B) at B=%d%n", slowOps, fastOps, B); } else { System.out.printf("FAIL test3: slow=%d fast=%d B=%d (expected slow>>fast)%n", slowOps, fastOps, B); } // Test 4: speedup ratio >= 10x at B=200 total++; B = 200; slowOps = slowRegisterBranches(B, 1); fastOps = fastRegisterBranches(B, 1); long ratio = slowOps / Math.max(fastOps, 1); if (ratio >= 10) { pass++; System.out.printf("PASS test4: speedup %dx at B=%d (slow=%d fast=%d)%n", ratio, B, slowOps, fastOps); } else { System.out.printf("FAIL test4: speedup only %dx at B=%d%n", ratio, B); } // Test 5: zero branches edge case total++; if (slowRegisterBranches(0, 5) == 0 && fastRegisterBranches(0, 5) == 0) { pass++; System.out.println("PASS test5: empty input"); } else { System.out.println("FAIL test5: empty input"); } System.out.printf("%n%d/%d PASS%n", pass, total); if (pass != total) System.exit(1); } }