package unit; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; /** * JSC-0001: BytecodeBasicBlock::computeImpl() O(B²×T) edge linking. * * Models the inner loop in BytecodeBasicBlock.cpp lines 181-193: * - SlowLinker: for each branch block, scan all B blocks and call * Vector::contains(jumpTargets) for each — O(B × T) per branch * - FastLinker: load jump targets into HashSet first — O(B + T) per branch * * Compile: javac -d . *.java (from defects/jsc/unit/) * Run: java -ea unit.JSCBytecodeBasicBlockTest */ public class JSCBytecodeBasicBlockTest { // ---- slow path: Vector linear contains -------------------------- static class SlowLinker { static long opCount; /** * @param blockCount total basic blocks (B) * @param jumpTargets list of target offsets (size T) * @return number of (block, target) linkage operations found */ static int link(int blockCount, List jumpTargets) { int linked = 0; // Simulate: for (auto& otherBlock : basicBlocks) for (int blockId = 0; blockId < blockCount; blockId++) { // Simulate: bytecodeOffsetsJumpedTo.contains(otherBlock.leaderOffset()) for (Integer target : jumpTargets) { opCount++; if (target == blockId) { linked++; break; } } } return linked; } } // ---- fast path: HashSet O(1) lookup ----------------------------- static class FastLinker { static long opCount; static int link(int blockCount, List jumpTargets) { // Build HashSet once: O(T) HashSet targetSet = new HashSet<>(jumpTargets); opCount += jumpTargets.size(); // insertions int linked = 0; // Simulate: for (auto& otherBlock : basicBlocks) for (int blockId = 0; blockId < blockCount; blockId++) { opCount++; // one hash lookup if (targetSet.contains(blockId)) { linked++; } } return linked; } } // ---- helpers ------------------------------------------------------------ static List makeTargets(int blockCount, int T) { // T evenly-spaced target block offsets List targets = new ArrayList<>(T); int step = Math.max(1, blockCount / T); for (int i = 0; i < T && i * step < blockCount; i++) targets.add(i * step); return targets; } // ---- tests -------------------------------------------------------------- static int passed = 0; static int total = 0; static void check(String name, boolean cond) { total++; if (cond) { passed++; } else { System.out.println("FAIL: " + name); } } public static void main(String[] args) { // correctness: both find same number of links { int B = 20, T = 4; List targets = makeTargets(B, T); SlowLinker.opCount = 0; FastLinker.opCount = 0; int slowLinks = SlowLinker.link(B, targets); int fastLinks = FastLinker.link(B, targets); check("slow and fast agree on link count", slowLinks == fastLinks); check("linked count == T", slowLinks == T); } // op-count scaling int[] blockCounts = {50, 100, 200, 500}; int[] switchSizes = {10, 50, 200, 500}; System.out.println(); System.out.printf("%-8s %-8s %14s %14s %10s%n", "B", "T", "slow_ops", "fast_ops", "ratio"); for (int B : blockCounts) { for (int T : switchSizes) { if (T > B) continue; List targets = makeTargets(B, T); SlowLinker.opCount = 0; FastLinker.opCount = 0; int slowLinks = SlowLinker.link(B, targets); int fastLinks = FastLinker.link(B, targets); check("results agree B=" + B + " T=" + T, slowLinks == fastLinks); long slow = SlowLinker.opCount; long fast = FastLinker.opCount; double ratio = (double) slow / fast; System.out.printf("%-8d %-8d %14d %14d %10.1f%n", B, T, slow, fast, ratio); // slow ops ~ O(B × T), fast ops ~ O(B + T) // ratio should grow with min(B,T) int minBT = Math.min(B, T); check("slow > fast for B=" + B + " T=" + T, slow > fast); // for large T, ratio should be at least T/4 if (T >= 50) { check("ratio >= T/4 for T=" + T, ratio >= (double) T / 4); } } } System.out.println(); System.out.printf("%d/%d PASS%n", passed, total); if (passed != total) System.exit(1); } }