wave15: v8-0002/0003 Intl+revectorizer (125x/25x) + bullet/box2d ticket files — 528/240

This commit is contained in:
russell@unturf.com 2026-03-27 19:16:28 -04:00
parent c4026333ba
commit 7146714143
13 changed files with 1520 additions and 11 deletions

View file

@ -0,0 +1,331 @@
package unit;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
/**
* V8Algorithm
*
* Unit tests for two CWE-407 defects in V8:
*
* v8-0002: Intl::CanonicalizeLocaleList src/objects/intl-objects.cc:940
* seen is a std::vector<std::string>; each locale membership check
* is O(seen.size()) total O(N²) for N input locales.
* Fix: parallel std::unordered_set for O(1) membership, keep vector
* for ordered output (ECMA-402 spec requires insertion order).
*
* v8-0003: SLPTree::TryReduceLoadChain src/compiler/revectorizer.cc:538
* loads is ZoneVector<Node*>; inner-loop membership check is
* std::find(loads.begin(), loads.end(), *it) O(N) per step
* O(N² × L) total for N loads, L effect-chain length.
* Fix: ZoneUnorderedSet<Node*> built once before the loops O(L).
*
* No external dependencies. Run with:
* javac -d . V8Algorithm.java && java -ea unit.V8Algorithm
*/
public class V8Algorithm {
// =======================================================================
// v8-0002: CanonicalizeLocaleList deduplication
// =======================================================================
/**
* Defective: ArrayList + linear contains mirrors std::vector::find on seen.
* Returns the number of string comparisons performed (instrumented).
*/
static long canonicalizeLocaleListSlow(String[] inputLocales) {
ArrayList<String> seen = new ArrayList<>();
long comparisons = 0;
for (String tag : inputLocales) {
// O(seen.size()) linear scan the defect
boolean found = false;
for (String existing : seen) {
comparisons++;
if (existing.equals(tag)) {
found = true;
break;
}
}
if (!found) {
seen.add(tag);
}
}
return comparisons;
}
/**
* Fixed: insertion-order LinkedHashSet for O(1) contains, preserving spec order.
* Returns the number of hash lookups performed (instrumented as 1 per input).
*/
static long canonicalizeLocaleListFast(String[] inputLocales) {
LinkedHashSet<String> seen = new LinkedHashSet<>();
long lookups = 0;
for (String tag : inputLocales) {
lookups++; // one O(1) hash lookup per input
seen.add(tag); // no-op if already present
}
return lookups;
}
/** Build a locale array: N entries cycling over `distinct` unique tags. */
static String[] makeLocales(int n, int distinct) {
String[] tags = new String[n];
for (int i = 0; i < n; i++) {
tags[i] = "locale-" + (i % distinct);
}
return tags;
}
// =======================================================================
// v8-0003: TryReduceLoadChain membership test
// =======================================================================
/**
* Defective: List.contains (O(N)) inside inner loop mirrors
* std::find(loads.begin(), loads.end(), *it).
*
* Simulates: for each load (N), walk a chain of L steps; at each step
* check membership with linear scan.
*
* Returns total comparison count.
*/
static long tryReduceLoadChainSlow(int numLoads, int chainLen) {
List<Integer> loads = new ArrayList<>();
for (int i = 0; i < numLoads; i++) loads.add(i);
long comparisons = 0;
for (int loadIdx = 0; loadIdx < numLoads; loadIdx++) {
// Walk the simulated effect chain
for (int step = 0; step < chainLen; step++) {
// Probe value cycles through loads to produce hits
int probe = step % numLoads;
// O(N) linear scan the defect
for (int j = 0; j < loads.size(); j++) {
comparisons++;
if (loads.get(j).equals(probe)) break;
}
}
}
return comparisons;
}
/**
* Fixed: HashSet built once; O(1) contains in inner loop.
* Returns total lookup count.
*/
static long tryReduceLoadChainFast(int numLoads, int chainLen) {
List<Integer> loads = new ArrayList<>();
for (int i = 0; i < numLoads; i++) loads.add(i);
HashSet<Integer> loadsSet = new HashSet<>(loads); // built once: O(N)
long lookups = 0;
for (int loadIdx = 0; loadIdx < numLoads; loadIdx++) {
for (int step = 0; step < chainLen; step++) {
int probe = step % numLoads;
lookups++; // O(1) hash lookup the fix
loadsSet.contains(probe);
}
}
return lookups;
}
// =======================================================================
// Tests v8-0002
// =======================================================================
static void test1_intl_correctness() {
// Both paths must return the same deduplication result.
int n = 20, distinct = 5;
String[] locales = makeLocales(n, distinct);
ArrayList<String> slowResult = new ArrayList<>();
for (String tag : locales) if (!slowResult.contains(tag)) slowResult.add(tag);
LinkedHashSet<String> fastResult = new LinkedHashSet<>();
for (String tag : locales) fastResult.add(tag);
assert slowResult.size() == fastResult.size()
: "result sizes differ: slow=" + slowResult.size() + " fast=" + fastResult.size();
assert slowResult.equals(new ArrayList<>(fastResult))
: "result order differs";
System.out.printf(
"test1 [v8-0002 correctness]: n=%d distinct=%d slow_size=%d fast_size=%d ORDER_PRESERVED%n",
n, distinct, slowResult.size(), fastResult.size());
}
static void test2_intl_ratio_at_n500() {
int n = 500, distinct = 250;
String[] locales = makeLocales(n, distinct);
long slow = canonicalizeLocaleListSlow(locales);
long fast = canonicalizeLocaleListFast(locales);
double ratio = (double) slow / Math.max(1, fast);
System.out.printf(
"test2 [v8-0002 ratio N=500]: slow_comparisons=%d fast_lookups=%d ratio=%.1fx%n",
slow, fast, ratio);
assert ratio >= 5.0
: "expected ratio >= 5x at N=500, got " + ratio;
}
static void test3_intl_all_distinct() {
int n = 500;
String[] locales = makeLocales(n, n); // all unique
long slow = canonicalizeLocaleListSlow(locales);
long fast = canonicalizeLocaleListFast(locales);
// All distinct: slow = 0+1+2+...+(n-1) = n*(n-1)/2
long expectedSlow = (long) n * (n - 1) / 2;
double ratio = (double) slow / Math.max(1, fast);
System.out.printf(
"test3 [v8-0002 all-distinct N=500]: slow=%d (expect=%d) fast=%d ratio=%.1fx%n",
slow, expectedSlow, fast, ratio);
assert slow == expectedSlow
: "slow comparisons=" + slow + " expected=" + expectedSlow;
assert ratio >= 100.0
: "expected ratio >= 100x for all-distinct N=500, got " + ratio;
}
static void test4_intl_scaling() {
// Doubling N (all distinct) should quadruple slow ops (O(N²)), double fast ops (O(N)).
int n1 = 200, n2 = 400;
// All distinct to get clean O(N²) vs O(N) growth
long s1 = canonicalizeLocaleListSlow(makeLocales(n1, n1));
long s2 = canonicalizeLocaleListSlow(makeLocales(n2, n2));
long f1 = canonicalizeLocaleListFast(makeLocales(n1, n1));
long f2 = canonicalizeLocaleListFast(makeLocales(n2, n2));
double slowGrowth = (double) s2 / Math.max(1, s1);
double fastGrowth = (double) f2 / Math.max(1, f1);
System.out.printf(
"test4 [v8-0002 scaling all-distinct]: slow 2x_N growth=%.2fx fast growth=%.2fx%n",
slowGrowth, fastGrowth);
// O(N²): doubling N ~4x ops; threshold 3x to allow small N effects
assert slowGrowth > 3.0
: "slow should grow ~quadratically on 2x N (all-distinct), got " + slowGrowth;
assert fastGrowth <= 2.5
: "fast should grow at most linearly on 2x N, got " + fastGrowth;
}
// =======================================================================
// Tests v8-0003
// =======================================================================
static void test5_revec_ratio_n16_chain100() {
int n = 16, chainLen = 100;
long slow = tryReduceLoadChainSlow(n, chainLen);
long fast = tryReduceLoadChainFast(n, chainLen);
double ratio = (double) slow / Math.max(1, fast);
System.out.printf(
"test5 [v8-0003 ratio N=16 L=100]: slow=%d fast=%d ratio=%.1fx%n",
slow, fast, ratio);
assert ratio >= 5.0
: "expected ratio >= 5x at N=16 L=100, got " + ratio;
}
static void test6_revec_ratio_n64_chain50() {
int n = 64, chainLen = 50;
long slow = tryReduceLoadChainSlow(n, chainLen);
long fast = tryReduceLoadChainFast(n, chainLen);
double ratio = (double) slow / Math.max(1, fast);
System.out.printf(
"test6 [v8-0003 ratio N=64 L=50]: slow=%d fast=%d ratio=%.1fx%n",
slow, fast, ratio);
assert ratio >= 20.0
: "expected ratio >= 20x at N=64 L=50, got " + ratio;
}
static void test7_revec_scaling() {
// Doubling N should roughly quadruple slow, double fast.
int chainLen = 50;
int n1 = 32, n2 = 64;
long s1 = tryReduceLoadChainSlow(n1, chainLen);
long s2 = tryReduceLoadChainSlow(n2, chainLen);
long f1 = tryReduceLoadChainFast(n1, chainLen);
long f2 = tryReduceLoadChainFast(n2, chainLen);
double slowGrowth = (double) s2 / Math.max(1, s1);
double fastGrowth = (double) f2 / Math.max(1, f1);
System.out.printf(
"test7 [v8-0003 scaling]: slow 2x_N growth=%.2fx fast growth=%.2fx%n",
slowGrowth, fastGrowth);
assert slowGrowth > 2.5
: "slow should grow super-linearly on 2x N, got " + slowGrowth;
assert fastGrowth <= 3.0
: "fast should grow at most linearly on 2x N, got " + fastGrowth;
}
static void test8_revec_correctness() {
// Slow and fast paths should agree on which probes are in the set.
int n = 20, chainLen = 30;
// Re-implement logic to check that both paths would accept the same elements.
List<Integer> loads = new ArrayList<>();
for (int i = 0; i < n; i++) loads.add(i);
HashSet<Integer> fastSet = new HashSet<>(loads);
long slowHits = 0, fastHits = 0;
for (int step = 0; step < chainLen; step++) {
int probe = step % n;
if (loads.contains(probe)) slowHits++;
if (fastSet.contains(probe)) fastHits++;
}
System.out.printf(
"test8 [v8-0003 correctness]: slow_hits=%d fast_hits=%d%n",
slowHits, fastHits);
assert slowHits == fastHits
: "slow and fast membership results disagree: slow=" + slowHits + " fast=" + fastHits;
}
// =======================================================================
// Main
// =======================================================================
public static void main(String[] args) {
System.out.println("=== V8Algorithm — CWE-407 unit tests ===");
System.out.println(" v8-0002: Intl::CanonicalizeLocaleList (intl-objects.cc:940)");
System.out.println(" v8-0003: SLPTree::TryReduceLoadChain (revectorizer.cc:538)");
System.out.println();
test1_intl_correctness();
System.out.println(" PASS test1_intl_correctness");
test2_intl_ratio_at_n500();
System.out.println(" PASS test2_intl_ratio_at_n500");
test3_intl_all_distinct();
System.out.println(" PASS test3_intl_all_distinct");
test4_intl_scaling();
System.out.println(" PASS test4_intl_scaling");
test5_revec_ratio_n16_chain100();
System.out.println(" PASS test5_revec_ratio_n16_chain100");
test6_revec_ratio_n64_chain50();
System.out.println(" PASS test6_revec_ratio_n64_chain50");
test7_revec_scaling();
System.out.println(" PASS test7_revec_scaling");
test8_revec_correctness();
System.out.println(" PASS test8_revec_correctness");
System.out.println();
System.out.println("8/8 PASS");
}
}