package unit; import java.util.ArrayList; import java.util.HashSet; /** * Unit tests modelling CWE-407 defects in LLVM: * * llvm-0002 — AliasSet::MemoryLocs: SmallVector + is_contained O(N) * vs DenseSet::count O(1). * Modelled as: outer loop over N memory accesses, inner * ArrayList.contains() for dedup vs HashSet.contains(). * * llvm-0003 — LCSSA isExitBlock: SmallVectorImpl + is_contained O(X) * vs SmallPtrSet::count O(1). * Modelled as: worklist of U uses, each calls * ArrayList.contains() for X exit blocks vs HashSet.contains(). * * Pure Java stdlib, instrumented operation counts. */ public class LlvmAliasSetTest { // ----------------------------------------------------------------------- // llvm-0002 helpers // ----------------------------------------------------------------------- /** Simulate defective path: N accesses, dedup via ArrayList.contains(). */ static long aliasSetDefective(int nAccesses, int maxLocs) { ArrayList memoryLocs = new ArrayList<>(); long ops = 0; for (int i = 0; i < nAccesses; i++) { long loc = i % maxLocs; // O(current size) scan — mirrors is_contained(MemoryLocs, MemLoc) boolean found = false; for (int j = 0; j < memoryLocs.size(); j++) { ops++; if (memoryLocs.get(j).equals(loc)) { found = true; break; } } if (!found) memoryLocs.add(loc); } return ops; } /** Simulate fixed path: N accesses, dedup via HashSet.contains(). */ static long aliasSetFixed(int nAccesses, int maxLocs) { HashSet memoryLocs = new HashSet<>(); long ops = 0; for (int i = 0; i < nAccesses; i++) { long loc = i % maxLocs; // O(1) — mirrors DenseSet::count(MemLoc) ops++; memoryLocs.add(loc); } return ops; } // ----------------------------------------------------------------------- // llvm-0003 helpers // ----------------------------------------------------------------------- /** Simulate defective LCSSA: U uses × X exit blocks, ArrayList scan. */ static long lcssaDefective(int nUses, int nExitBlocks) { ArrayList exitBlocks = new ArrayList<>(); for (int i = 0; i < nExitBlocks; i++) exitBlocks.add(i); long ops = 0; for (int u = 0; u < nUses; u++) { int userBB = u % nExitBlocks; // O(X) per use — mirrors is_contained(ExitBlocks, UserBB) for (int j = 0; j < exitBlocks.size(); j++) { ops++; if (exitBlocks.get(j).equals(userBB)) break; } } return ops; } /** Simulate fixed LCSSA: U uses × X exit blocks, HashSet lookup. */ static long lcssaFixed(int nUses, int nExitBlocks) { HashSet exitBlockSet = new HashSet<>(); for (int i = 0; i < nExitBlocks; i++) exitBlockSet.add(i); long ops = 0; for (int u = 0; u < nUses; u++) { int userBB = u % nExitBlocks; // O(1) — mirrors SmallPtrSet::count(UserBB) ops++; exitBlockSet.contains(userBB); } return ops; } // ----------------------------------------------------------------------- // Test methods // ----------------------------------------------------------------------- /** * llvm-0002: defective op count must grow quadratically with saturation. * At N=200 accesses, maxLocs=250: defective scans accumulate O(N×locs). */ static void testAliasSetDefectiveCountGrows() { long opsSmall = aliasSetDefective(50, 250); long opsFull = aliasSetDefective(200, 250); // Quadratic growth: opsFull should be >> 4× opsSmall assert opsFull > opsSmall * 4 : "llvm-0002: expected quadratic growth, got opsSmall=" + opsSmall + " opsFull=" + opsFull; System.out.printf("PASS testAliasSetDefectiveCountGrows: opsSmall=%d opsFull=%d ratio=%.1fx%n", opsSmall, opsFull, (double) opsFull / opsSmall); } /** * llvm-0002: fixed op count must be O(N) (one op per access). * At N=200 accesses: ops == N. */ static void testAliasSetFixedCountLinear() { int n = 200; long ops = aliasSetFixed(n, 250); assert ops == n : "llvm-0002: expected ops==" + n + " got " + ops; System.out.printf("PASS testAliasSetFixedCountLinear: ops=%d (expected %d)%n", ops, n); } /** * llvm-0002: speedup ratio defective/fixed must exceed 10× at N=200. */ static void testAliasSetSpeedupRatio() { int n = 200, maxLocs = 250; long defOps = aliasSetDefective(n, maxLocs); long fixOps = aliasSetFixed(n, maxLocs); double ratio = (double) defOps / fixOps; assert ratio > 10.0 : "llvm-0002: speedup ratio " + ratio + " not > 10x"; System.out.printf("PASS testAliasSetSpeedupRatio: defective=%d fixed=%d ratio=%.1fx%n", defOps, fixOps, ratio); } /** * llvm-0003: LCSSA defective op count grows as U×X. * At U=100 uses, X=20 exit blocks: ops ≈ U×(X/2) on average. */ static void testLcssaDefectiveCountGrows() { long opsSmall = lcssaDefective(25, 20); long opsFull = lcssaDefective(100, 20); assert opsFull > opsSmall * 3 : "llvm-0003: expected super-linear growth, got opsSmall=" + opsSmall + " opsFull=" + opsFull; System.out.printf("PASS testLcssaDefectiveCountGrows: opsSmall=%d opsFull=%d ratio=%.1fx%n", opsSmall, opsFull, (double) opsFull / opsSmall); } /** * llvm-0003: speedup ratio defective/fixed must exceed 5× at U=100, X=20. */ static void testLcssaSpeedupRatio() { int nUses = 100, nExitBlocks = 20; long defOps = lcssaDefective(nUses, nExitBlocks); long fixOps = lcssaFixed(nUses, nExitBlocks); double ratio = (double) defOps / fixOps; assert ratio > 5.0 : "llvm-0003: speedup ratio " + ratio + " not > 5x"; System.out.printf("PASS testLcssaSpeedupRatio: defective=%d fixed=%d ratio=%.1fx%n", defOps, fixOps, ratio); } // ----------------------------------------------------------------------- // Main // ----------------------------------------------------------------------- public static void main(String[] args) { System.out.println("=== LlvmAliasSetTest ==="); testAliasSetDefectiveCountGrows(); testAliasSetFixedCountLinear(); testAliasSetSpeedupRatio(); testLcssaDefectiveCountGrows(); testLcssaSpeedupRatio(); System.out.println("All tests passed."); } }