package unit; import java.util.ArrayList; import java.util.HashMap; import java.util.List; /** * SM-0002: MDefinitionRemapper::lookup() O(N) linear scan inside loop-unroll cloning. * * Models: * - SlowRemapper — Vector linear lookup, O(V) per lookup → O(V²) per body clone * - FastRemapper — HashMap lookup, O(1) per lookup → O(V) per body clone * * Compile: javac -d . *.java (from defects/spidermonkey/unit/) * Run: java -ea unit.SpiderMonkeyRemapperTest */ public class SpiderMonkeyRemapperTest { // ---- slow path: Vector linear scan -------------------------------- static class SlowRemapper { static long opCount; static class Pair { final int original; int replacement; Pair(int o) { original = o; replacement = o; } } private final List pairs = new ArrayList<>(); void enregister(int original) { pairs.add(new Pair(original)); } int lookup(int original) { for (Pair p : pairs) { opCount++; if (p.original == original) return p.replacement; } return -1; // not found } void update(int original, int replacement) { for (Pair p : pairs) { if (p.original == original) { p.replacement = replacement; return; } } } } // ---- fast path: HashMap O(1) lookup ------------------------------------- static class FastRemapper { static long opCount; private final HashMap map = new HashMap<>(); void enregister(int original) { map.put(original, original); } int lookup(int original) { opCount++; // one hash lookup Integer v = map.get(original); return v == null ? -1 : v; } void update(int original, int replacement) { map.put(original, replacement); } } // ---- simulate loop body clone ------------------------------------------- // For each of V values in the mapper, clone K operands that each call lookup(). // Operands reference value (V-1) — the last registered — to hit worst-case // scan length for SlowRemapper (must scan entire list every time). static long simulateClone(boolean fast, int V, int K) { int worstCaseKey = V - 1; // last in the vector → full scan each time if (fast) { FastRemapper.opCount = 0; FastRemapper mapper = new FastRemapper(); for (int i = 0; i < V; i++) mapper.enregister(i); // clone V instructions, each with K operands for (int insn = 0; insn < V; insn++) { for (int op = 0; op < K; op++) { mapper.lookup(worstCaseKey); } } return FastRemapper.opCount; } else { SlowRemapper.opCount = 0; SlowRemapper mapper = new SlowRemapper(); for (int i = 0; i < V; i++) mapper.enregister(i); for (int insn = 0; insn < V; insn++) { for (int op = 0; op < K; op++) { mapper.lookup(worstCaseKey); } } return SlowRemapper.opCount; } } // ---- 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 return same result { SlowRemapper slow = new SlowRemapper(); FastRemapper fast = new FastRemapper(); int[] vals = {10, 20, 30, 40, 50}; for (int v : vals) { slow.enregister(v); fast.enregister(v); } slow.update(20, 99); fast.update(20, 99); check("slow lookup 10", slow.lookup(10) == 10); check("fast lookup 10", fast.lookup(10) == 10); check("slow lookup 20 updated", slow.lookup(20) == 99); check("fast lookup 20 updated", fast.lookup(20) == 99); check("slow lookup 50", slow.lookup(50) == 50); check("fast lookup 50", fast.lookup(50) == 50); check("slow lookup miss", slow.lookup(99) == -1); check("fast lookup miss", fast.lookup(99) == -1); } // op-count scaling: V values, K=3 operands each int[] sizes = {10, 30, 60, 100, 150}; int K = 3; System.out.println(); System.out.printf("%-8s %12s %12s %8s%n", "V", "slow_ops", "fast_ops", "ratio"); for (int V : sizes) { long slowOps = simulateClone(false, V, K); long fastOps = simulateClone(true, V, K); double ratio = (double) slowOps / fastOps; System.out.printf("%-8d %12d %12d %8.1f%n", V, slowOps, fastOps, ratio); // slow ops should be O(V²): roughly V * K * V/2 on average // fast ops should be O(V): exactly V * K long expectedFast = (long) V * K; // slow worst-case: each lookup scans all V entries (key is last) long expectedSlow = (long) V * K * V; check("fast ops == V*K for V=" + V, fastOps == expectedFast); check("slow ops == V*K*V for V=" + V, slowOps == expectedSlow); // ratio should be exactly V (slow scans V items per lookup) check("slow/fast ratio == V for V=" + V, Math.abs(ratio - V) < 0.01); } System.out.println(); System.out.printf("%d/%d PASS%n", passed, total); if (passed != total) System.exit(1); } }