wave15: sm-0003/0004 + threejs-0006 + varnish-0002 + mongodb-0008 — 533/240
This commit is contained in:
parent
7146714143
commit
31850d5ef6
13 changed files with 1893 additions and 9 deletions
309
defects/spidermonkey/unit/SpiderMonkeySimpleSetTest.java
Normal file
309
defects/spidermonkey/unit/SpiderMonkeySimpleSetTest.java
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
package unit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* SpiderMonkeySimpleSetTest
|
||||
*
|
||||
* Models the CWE-407 defect in UnrollLoops.cpp SimpleSet<T,N,AP>:
|
||||
*
|
||||
* Defective: mozilla::Vector<T,N,AP> backed set.
|
||||
* contains() iterates all N elements linearly.
|
||||
* add() calls contains() before appending.
|
||||
* Called inside triple-nested loop (copies × blocks × values).
|
||||
*
|
||||
* Fixed: HashSet<T> backed set.
|
||||
* contains() / add() are O(1) amortised.
|
||||
*
|
||||
* Models ValueSet (inline cap = 64) and its use in the unrolling phase.
|
||||
* "Value" is modelled as an Integer ID. Comparison counts are instrumented
|
||||
* explicitly — not wall-clock timing.
|
||||
*
|
||||
* Run: javac -d . SpiderMonkeySimpleSetTest.java && java -ea unit.SpiderMonkeySimpleSetTest
|
||||
*/
|
||||
public class SpiderMonkeySimpleSetTest {
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Defective SimpleSet: ArrayList<Integer> with linear contains()
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
static class DefectiveSimpleSet {
|
||||
private final ArrayList<Integer> vec = new ArrayList<>();
|
||||
long comparisons = 0;
|
||||
|
||||
boolean contains(int t) {
|
||||
for (int existing : vec) {
|
||||
comparisons++;
|
||||
if (existing == t) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean add(int t) {
|
||||
if (contains(t)) return true;
|
||||
vec.add(t);
|
||||
return true;
|
||||
}
|
||||
|
||||
int size() { return vec.size(); }
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Fixed SimpleSet: HashSet<Integer> with O(1) contains()
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
static class FixedSimpleSet {
|
||||
private final HashSet<Integer> set = new HashSet<>();
|
||||
long lookups = 0;
|
||||
|
||||
boolean contains(int t) {
|
||||
lookups++;
|
||||
return set.contains(t);
|
||||
}
|
||||
|
||||
boolean add(int t) {
|
||||
lookups++;
|
||||
set.add(t);
|
||||
return true;
|
||||
}
|
||||
|
||||
int size() { return set.size(); }
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Simulate the unrolling inner loop:
|
||||
// unrollFactor copies × numBlocks blocks × numSuccessors successors
|
||||
// exitTargetBlocks.contains(succ) called per successor per block per copy
|
||||
// exitingValues.contains(value) called per value per block per copy
|
||||
//
|
||||
// Parameters:
|
||||
// unrollFactor - number of loop body copies (typically 2-4)
|
||||
// numBlocks - basic blocks in the loop body
|
||||
// numSuccessors - successors per block's last instruction
|
||||
// numExitTargets - size of exitTargetBlocks set (B, up to 8)
|
||||
// numValues - size of exitingValues set (V, up to 64)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
static long simulateUnrollDefective(int unrollFactor, int numBlocks,
|
||||
int numSuccessors, int numExitTargets,
|
||||
int numValues) {
|
||||
DefectiveSimpleSet exitTargetBlocks = new DefectiveSimpleSet();
|
||||
DefectiveSimpleSet exitingValues = new DefectiveSimpleSet();
|
||||
|
||||
// Populate sets
|
||||
for (int i = 0; i < numExitTargets; i++) exitTargetBlocks.add(i);
|
||||
for (int i = 0; i < numValues; i++) exitingValues.add(i);
|
||||
|
||||
long ops = 0;
|
||||
|
||||
for (int cix = 0; cix < unrollFactor; cix++) {
|
||||
for (int bix = 0; bix < numBlocks; bix++) {
|
||||
// Successor scan — check if successor is an exit target
|
||||
for (int s = 0; s < numSuccessors; s++) {
|
||||
int succ = s % (numExitTargets + 2); // most are not exits
|
||||
exitTargetBlocks.contains(succ);
|
||||
}
|
||||
// Value scan — check if each value is an exiting value
|
||||
for (int v = 0; v < numValues; v++) {
|
||||
exitingValues.contains(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ops = exitTargetBlocks.comparisons + exitingValues.comparisons;
|
||||
return ops;
|
||||
}
|
||||
|
||||
static long simulateUnrollFixed(int unrollFactor, int numBlocks,
|
||||
int numSuccessors, int numExitTargets,
|
||||
int numValues) {
|
||||
FixedSimpleSet exitTargetBlocks = new FixedSimpleSet();
|
||||
FixedSimpleSet exitingValues = new FixedSimpleSet();
|
||||
|
||||
for (int i = 0; i < numExitTargets; i++) exitTargetBlocks.add(i);
|
||||
for (int i = 0; i < numValues; i++) exitingValues.add(i);
|
||||
|
||||
long ops = 0;
|
||||
|
||||
for (int cix = 0; cix < unrollFactor; cix++) {
|
||||
for (int bix = 0; bix < numBlocks; bix++) {
|
||||
for (int s = 0; s < numSuccessors; s++) {
|
||||
int succ = s % (numExitTargets + 2);
|
||||
exitTargetBlocks.contains(succ);
|
||||
}
|
||||
for (int v = 0; v < numValues; v++) {
|
||||
exitingValues.contains(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ops = exitTargetBlocks.lookups + exitingValues.lookups;
|
||||
return ops;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Test 1 — correctness: defective and fixed sets agree on membership
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
static void test1_correctness() {
|
||||
DefectiveSimpleSet slow = new DefectiveSimpleSet();
|
||||
FixedSimpleSet fast = new FixedSimpleSet();
|
||||
|
||||
int[] values = {10, 20, 30, 10, 40, 20};
|
||||
for (int v : values) {
|
||||
slow.add(v);
|
||||
fast.add(v);
|
||||
}
|
||||
|
||||
// Both should have 4 distinct values
|
||||
assert slow.size() == 4 : "defective size expected 4, got " + slow.size();
|
||||
assert fast.size() == 4 : "fixed size expected 4, got " + fast.size();
|
||||
|
||||
// Both should contain all inserted values
|
||||
for (int v : new int[]{10, 20, 30, 40}) {
|
||||
assert slow.contains(v) : "defective missing " + v;
|
||||
assert fast.contains(v) : "fixed missing " + v;
|
||||
}
|
||||
|
||||
// Both should not contain absent values
|
||||
assert !slow.contains(99) : "defective should not contain 99";
|
||||
assert !fast.contains(99) : "fixed should not contain 99";
|
||||
|
||||
System.out.printf("test1: size=%d (both agree), membership checks PASS%n",
|
||||
slow.size());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Test 2 — ratio >= 5x at V=64, unrollFactor=4, numBlocks=20
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
static void test2_ratioAtMaxValueSet() {
|
||||
int unrollFactor = 4;
|
||||
int numBlocks = 20;
|
||||
int numSuccessors = 3;
|
||||
int numExitTargets = 8; // BlockSet inline cap
|
||||
int numValues = 64; // ValueSet inline cap (MaxValuesForPeel)
|
||||
|
||||
long defectOps = simulateUnrollDefective(unrollFactor, numBlocks,
|
||||
numSuccessors, numExitTargets, numValues);
|
||||
long fixedOps = simulateUnrollFixed(unrollFactor, numBlocks,
|
||||
numSuccessors, numExitTargets, numValues);
|
||||
|
||||
double ratio = (double) defectOps / Math.max(1, fixedOps);
|
||||
System.out.printf("test2: unroll=%d blocks=%d V=%d defect=%d fixed=%d ratio=%.1fx%n",
|
||||
unrollFactor, numBlocks, numValues, defectOps, fixedOps, ratio);
|
||||
|
||||
assert ratio >= 5.0 : "expected ratio >= 5x, got " + ratio;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Test 3 — worst-case simulation: V=64, 4 copies, 50 blocks
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
static void test3_worstCase() {
|
||||
int unrollFactor = 4;
|
||||
int numBlocks = 50;
|
||||
int numSuccessors = 4;
|
||||
int numExitTargets = 8;
|
||||
int numValues = 64;
|
||||
|
||||
long defectOps = simulateUnrollDefective(unrollFactor, numBlocks,
|
||||
numSuccessors, numExitTargets, numValues);
|
||||
long fixedOps = simulateUnrollFixed(unrollFactor, numBlocks,
|
||||
numSuccessors, numExitTargets, numValues);
|
||||
|
||||
double ratio = (double) defectOps / Math.max(1, fixedOps);
|
||||
System.out.printf("test3: worst-case unroll=%d blocks=%d V=%d defect=%d fixed=%d ratio=%.1fx%n",
|
||||
unrollFactor, numBlocks, numValues, defectOps, fixedOps, ratio);
|
||||
|
||||
assert ratio >= 10.0 : "expected ratio >= 10x at worst case, got " + ratio;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Test 4 — scaling: doubling V roughly doubles defect ops, fixed stays constant
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
static void test4_linearScalingDefect() {
|
||||
int unrollFactor = 4;
|
||||
int numBlocks = 20;
|
||||
int numSuccessors = 2;
|
||||
int numExitTargets = 4;
|
||||
|
||||
long d32 = simulateUnrollDefective(unrollFactor, numBlocks,
|
||||
numSuccessors, numExitTargets, 32);
|
||||
long d64 = simulateUnrollDefective(unrollFactor, numBlocks,
|
||||
numSuccessors, numExitTargets, 64);
|
||||
long f32 = simulateUnrollFixed(unrollFactor, numBlocks,
|
||||
numSuccessors, numExitTargets, 32);
|
||||
long f64 = simulateUnrollFixed(unrollFactor, numBlocks,
|
||||
numSuccessors, numExitTargets, 64);
|
||||
|
||||
double defectGrowth = (double) d64 / Math.max(1, d32);
|
||||
double fixedGrowth = (double) f64 / Math.max(1, f32);
|
||||
|
||||
System.out.printf("test4: V=32→64 defect %d→%d (%.2fx) fixed %d→%d (%.2fx)%n",
|
||||
d32, d64, defectGrowth, f32, f64, fixedGrowth);
|
||||
|
||||
// Defect ops should grow faster than fixed when V doubles
|
||||
assert defectGrowth > fixedGrowth
|
||||
: "defect should grow faster than fixed when V doubles";
|
||||
// Fixed should grow roughly linearly in V (more lookups, each O(1))
|
||||
assert fixedGrowth <= 2.5
|
||||
: "fixed growth when V doubles should be near 2x, got " + fixedGrowth;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Test 5 — add() dedup correctness under repeated inserts
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
static void test5_addDedup() {
|
||||
DefectiveSimpleSet slow = new DefectiveSimpleSet();
|
||||
FixedSimpleSet fast = new FixedSimpleSet();
|
||||
|
||||
// Insert values 0..9 three times each
|
||||
for (int round = 0; round < 3; round++) {
|
||||
for (int v = 0; v < 10; v++) {
|
||||
slow.add(v);
|
||||
fast.add(v);
|
||||
}
|
||||
}
|
||||
|
||||
// Both sets should contain exactly 10 distinct values
|
||||
assert slow.size() == 10
|
||||
: "defective: expected 10 unique values, got " + slow.size();
|
||||
assert fast.size() == 10
|
||||
: "fixed: expected 10 unique values, got " + fast.size();
|
||||
|
||||
System.out.printf("test5: add() dedup correct, size=%d (both)%n", slow.size());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Main
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== SpiderMonkeySimpleSetTest ===");
|
||||
System.out.println("Modelling CWE-407 sm-0003: SimpleSet::contains() linear scan vs HashSet O(1)");
|
||||
System.out.println("Location: js/src/jit/UnrollLoops.cpp SimpleSet<T,N,AP>");
|
||||
System.out.println();
|
||||
|
||||
test1_correctness();
|
||||
System.out.println(" PASS test1_correctness");
|
||||
|
||||
test2_ratioAtMaxValueSet();
|
||||
System.out.println(" PASS test2_ratioAtMaxValueSet");
|
||||
|
||||
test3_worstCase();
|
||||
System.out.println(" PASS test3_worstCase");
|
||||
|
||||
test4_linearScalingDefect();
|
||||
System.out.println(" PASS test4_linearScalingDefect");
|
||||
|
||||
test5_addDedup();
|
||||
System.out.println(" PASS test5_addDedup");
|
||||
|
||||
System.out.println();
|
||||
System.out.println("5/5 PASS");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue