java-topology/defects/rustc/unit/TargetFeatureCallSafeAlgorithm.java

219 lines
8.8 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package unit;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Models rustc's is_target_feature_call_safe() —
* checks whether all features required by the callee are present in the caller's body.
*
* SLOW: O(C × B) — for each callee feature, scan all body features linearly.
* FAST: O(C + B) — build a HashSet from body features once, then O(1) per callee lookup.
*
* CWE-407: compiler/rustc_middle/src/ty/context.rs:1323-1325
*/
public class TargetFeatureCallSafeAlgorithm {
// -------------------------------------------------------------------------
// Slow (defective) implementation — mirrors current rustc code
// -------------------------------------------------------------------------
static class SlowChecker {
long linearScans = 0;
/**
* Returns true if all callee features are present in bodyFeatures.
* O(callee.size() × body.size()) — Vec<TargetFeature> nested linear scan.
*/
boolean isSafe(List<String> calleeFeatures, List<String> bodyFeatures) {
for (String callee : calleeFeatures) {
boolean found = false;
for (String body : bodyFeatures) {
linearScans++;
if (body.equals(callee)) {
found = true;
break;
}
}
if (!found) return false;
}
return true;
}
}
// -------------------------------------------------------------------------
// Fast (fixed) implementation — HashSet for O(1) membership
// -------------------------------------------------------------------------
static class FastChecker {
long hashLookups = 0;
/**
* Returns true if all callee features are present in bodyFeatures.
* O(C + B) — build HashSet once, then O(1) per callee feature.
*/
boolean isSafe(List<String> calleeFeatures, List<String> bodyFeatures) {
Set<String> bodySet = new HashSet<>(bodyFeatures);
for (String callee : calleeFeatures) {
hashLookups++;
if (!bodySet.contains(callee)) return false;
}
return true;
}
}
// -------------------------------------------------------------------------
// Test helpers
// -------------------------------------------------------------------------
static int passed = 0;
static int total = 0;
static void check(String desc, boolean cond) {
total++;
if (cond) {
passed++;
System.out.printf(" PASS %s%n", desc);
} else {
System.out.printf(" FAIL %s%n", desc);
}
}
/** Simulate x86-64 target features (subset of actual rustc list). */
static List<String> buildPlatformFeatures(int count) {
String[] base = {
"avx512f", "avx512cd", "avx512bw", "avx512dq", "avx512vl",
"avx512ifma", "avx512vbmi", "avx512vnni", "avx512bf16", "avx512vp2intersect",
"avx2", "avx", "fma", "bmi1", "bmi2", "lzcnt", "popcnt",
"sse4.1", "sse4.2", "sse4a", "ssse3", "sse3", "sse2", "sse",
"aes", "pclmulqdq", "sha", "rdrnd", "rdseed", "adx",
"f16c", "fxsr", "xsave", "xsaveopt", "xsavec", "xsaves",
"cx16", "sahf", "movbe", "cmpxchg16b", "clflushopt", "clwb",
"rtm", "hle", "tbm", "3dnow", "3dnowa", "mmx", "abm",
"ermsb", "fsrm", "vpclmulqdq", "vaes", "gfni", "avxvnni",
"amx-tile", "amx-int8", "amx-bf16", "amx-fp16",
"serialize", "tsxldtrk", "uintr", "kl", "widekl",
};
List<String> result = new ArrayList<>();
for (int i = 0; i < Math.min(count, base.length); i++) {
result.add(base[i]);
}
return result;
}
public static void main(String[] args) {
System.out.println("TargetFeatureCallSafeAlgorithm — CWE-407 unit test");
System.out.println("rustc-0003: is_target_feature_call_safe O(C×B) → O(C+B)");
System.out.println();
SlowChecker slow = new SlowChecker();
FastChecker fast = new FastChecker();
// --- Correctness: safe call (callee subset of body) ---
{
List<String> callee = List.of("avx2", "bmi1", "popcnt");
List<String> body = List.of("avx2", "avx", "bmi1", "bmi2", "popcnt", "sse4.1");
boolean slowResult = slow.isSafe(callee, body);
boolean fastResult = fast.isSafe(callee, body);
check("safe-call: slow returns true", slowResult);
check("safe-call: fast returns true", fastResult);
check("safe-call: results agree", slowResult == fastResult);
}
// --- Correctness: unsafe call (callee has feature body lacks) ---
{
List<String> callee = List.of("avx512f", "avx512bw");
List<String> body = List.of("avx2", "avx", "bmi1");
boolean slowResult = slow.isSafe(callee, body);
boolean fastResult = fast.isSafe(callee, body);
check("unsafe-call: slow returns false", !slowResult);
check("unsafe-call: fast returns false", !fastResult);
check("unsafe-call: results agree", slowResult == fastResult);
}
// --- Correctness: empty callee (always safe) ---
{
List<String> callee = List.of();
List<String> body = List.of("avx2");
boolean slowResult = slow.isSafe(callee, body);
boolean fastResult = fast.isSafe(callee, body);
check("empty-callee: slow returns true", slowResult);
check("empty-callee: fast returns true", fastResult);
}
// --- Correctness: empty body, non-empty callee (always unsafe) ---
{
List<String> callee = List.of("avx2");
List<String> body = List.of();
boolean slowResult = slow.isSafe(callee, body);
boolean fastResult = fast.isSafe(callee, body);
check("empty-body: slow returns false", !slowResult);
check("empty-body: fast returns false", !fastResult);
}
// --- Performance: O(C×B) vs O(C+B) ---
{
// Simulate a function with 50 callee features all present at the END of a 60-feature
// body — forces slow checker to scan to the last position for each callee feature,
// maximising the per-call comparison count to ~C×B.
int C = 50;
int B = 60;
// callee features are the LAST C entries of the B-element body list
List<String> fullBody = buildPlatformFeatures(B);
List<String> body = new ArrayList<>(fullBody);
// callee = last C elements (appear near end of body, maximise scan length)
List<String> callee = new ArrayList<>(fullBody.subList(B - C, B));
// Reverse body so callee features are at the back
java.util.Collections.reverse(body);
// Warm up JIT
for (int i = 0; i < 500; i++) slow.isSafe(callee, body);
for (int i = 0; i < 500; i++) fast.isSafe(callee, body);
// Reset counters
slow.linearScans = 0;
fast.hashLookups = 0;
int RUNS = 20_000;
long t0 = System.nanoTime();
for (int i = 0; i < RUNS; i++) slow.isSafe(callee, body);
long slowNs = System.nanoTime() - t0;
long t1 = System.nanoTime();
for (int i = 0; i < RUNS; i++) fast.isSafe(callee, body);
long fastNs = System.nanoTime() - t1;
double ratio = (double) slowNs / fastNs;
System.out.printf(" INFO C=%d B=%d slow_scans=%d fast_lookups=%d ratio=%.1fx%n",
C, B, slow.linearScans, fast.hashLookups, ratio);
// Slow: each callee feature is at the back of body → scans many positions each.
// Minimum acceptable: at least C*(B/4)*RUNS (conservative lower bound, early-exit aware)
check("slow does >> C×RUNS comparisons (linear scan dominates)",
slow.linearScans >= (long) C * (B / 4) * RUNS);
// Fast: exactly C hash lookups per call
check("fast does exactly C×RUNS hash lookups",
fast.hashLookups == (long) C * RUNS);
// Op ratio: slow does at least B/2 per callee feature, fast does 1
long slowOpsPerRun = slow.linearScans / RUNS;
long fastOpsPerRun = fast.hashLookups / RUNS;
check("slow op count >> fast op count (>= 10x)",
slowOpsPerRun >= fastOpsPerRun * 10);
}
System.out.println();
System.out.printf("%d/%d PASS%n", passed, total);
if (passed != total) System.exit(1);
}
}