From 460e845aed6233fa584c8cc82b779e44b30867aa Mon Sep 17 00:00:00 2001 From: yusufcanislek Date: Wed, 1 Apr 2026 06:55:56 +0300 Subject: [PATCH] fix: stabilize full-handler oracle fixtures --- docs/REWRITE_BASELINE.md | 1 + lifter/test/test_vectors/oracle_vectors.json | 1 - .../oracle_vectors_full_handlers.json | 35 ++++++++++++++++++- scripts/rewrite/generate_oracle_vectors.py | 16 ++++++--- .../rewrite/oracle_seed_full_handlers.json | 32 +++++++++++++++++ .../oracle_seed_full_handlers_enriched.json | 34 ++++++++++++++++++ 6 files changed, 113 insertions(+), 6 deletions(-) diff --git a/docs/REWRITE_BASELINE.md b/docs/REWRITE_BASELINE.md index 975fe33..5c4d934 100644 --- a/docs/REWRITE_BASELINE.md +++ b/docs/REWRITE_BASELINE.md @@ -57,6 +57,7 @@ Use `run_microtests.cmd --check-flags ` to enforce oracle flag compariso Use `run_microtests.cmd --build ` to force rebuilding `rewrite_microtests.exe`, or `run_microtests.cmd --no-build ` to skip any build step. Set `SKIP_ORACLE_GENERATION=1` to reuse a pre-generated oracle file. Set `MERGEN_TEST_VECTORS=` to point tests at a custom oracle JSON file. Use `run_all_handlers.cmd` to exercise full handler coverage smoke tests. It writes `lifter/test_vectors/oracle_vectors_full_handlers.json` and then runs microtests against it through `run_microtests.cmd` (which now builds lazily). +Oracle vector JSON fixtures are deterministic by design; regenerating them should only change tracked files when the underlying cases change, not because of wall-clock metadata. Full-handler vectors are expected to execute end-to-end (no default `skip: true` crash exclusions). Use `run_flagstress.cmd` (or `python test.py flags`) for broad strict-flag validation across all handlers that explicitly write flags. Use `python test.py semantic` to run runtime semantic regression for all samples (accepts `--filter` to narrow scope and `--input-ir` to override the IR file for a single sample). diff --git a/lifter/test/test_vectors/oracle_vectors.json b/lifter/test/test_vectors/oracle_vectors.json index 7ded230..127a65f 100644 --- a/lifter/test/test_vectors/oracle_vectors.json +++ b/lifter/test/test_vectors/oracle_vectors.json @@ -1,6 +1,5 @@ { "schema": "mergen-oracle-v1", - "generated_at_utc": "2026-03-23T01:08:54.375218+00:00", "source_seed_schema": "mergen-oracle-seed-v1", "providers": [ "unicorn" diff --git a/lifter/test/test_vectors/oracle_vectors_full_handlers.json b/lifter/test/test_vectors/oracle_vectors_full_handlers.json index b32db72..2cdf4c1 100644 --- a/lifter/test/test_vectors/oracle_vectors_full_handlers.json +++ b/lifter/test/test_vectors/oracle_vectors_full_handlers.json @@ -1,6 +1,5 @@ { "schema": "mergen-oracle-v1", - "generated_at_utc": "2026-03-23T02:39:45.593139+00:00", "source_seed_schema": "mergen-oracle-seed-v1", "providers": [ "unicorn" @@ -4493,6 +4492,40 @@ } } }, + { + "name": "smoke_scasx_scasq", + "handler": "scasx", + "oracle_mode": "none", + "instruction_bytes": [ + 73, + 175 + ], + "initial": { + "registers": { + "RAX": "0x1122334455667788", + "RBX": "0x8877665544332211", + "RCX": "0x10", + "RDX": "0x2" + }, + "flags": { + "FLAG_CF": 0, + "FLAG_PF": 0, + "FLAG_AF": 0, + "FLAG_ZF": 0, + "FLAG_SF": 0, + "FLAG_OF": 0, + "FLAG_DF": 0, + "FLAG_IF": 1 + } + }, + "expected": { + "registers": {}, + "flags": {} + }, + "oracle_observations": {}, + "skip": true, + "skip_reason": "memory operand in 'scasq rax, qword ptr [rdi]'; needs mapped memory for emulation" + }, { "name": "smoke_setb_setb", "handler": "setb", diff --git a/scripts/rewrite/generate_oracle_vectors.py b/scripts/rewrite/generate_oracle_vectors.py index f1662b6..e768ca6 100644 --- a/scripts/rewrite/generate_oracle_vectors.py +++ b/scripts/rewrite/generate_oracle_vectors.py @@ -4,7 +4,6 @@ import importlib import json import sys from dataclasses import dataclass -from datetime import datetime, timezone from pathlib import Path from typing import Dict, List, Tuple @@ -176,6 +175,12 @@ def load_seed(path: Path) -> dict: return payload +def _format_register_hex(reg_name: str, value: int) -> str: + if reg_name.upper().startswith("XMM"): + return f"0x{value:032x}" + return f"0x{value:x}" + + def normalize_expected(case: dict, oracle_result: OracleResult) -> dict: expected = case.get("expected", {}) out = { @@ -184,7 +189,9 @@ def normalize_expected(case: dict, oracle_result: OracleResult) -> dict: } for reg_name in expected.get("registers", {}).keys(): - out["registers"][reg_name] = f"0x{oracle_result.registers[reg_name]:x}" + out["registers"][reg_name] = _format_register_hex( + reg_name, oracle_result.registers[reg_name] + ) for flag_name in expected.get("flags", {}).keys(): out["flags"][flag_name] = int(oracle_result.flags[flag_name]) @@ -218,9 +225,10 @@ def compare_results(results: Dict[str, OracleResult], case_name: str, *, strict: def build_output(seed_payload: dict, provider_names: List[str], output_cases: List[dict]) -> dict: + # These oracle fixtures are checked into the repo, so wall-clock metadata + # would create meaningless diffs on every regeneration. return { "schema": "mergen-oracle-v1", - "generated_at_utc": datetime.now(timezone.utc).isoformat(), "source_seed_schema": seed_payload["schema"], "providers": provider_names, "cases": output_cases, @@ -371,7 +379,7 @@ def main(): "oracle_observations": { provider_name: { "registers": { - reg: f"0x{value:x}" + reg: _format_register_hex(reg, value) for reg, value in result.registers.items() }, "flags": result.flags, diff --git a/scripts/rewrite/oracle_seed_full_handlers.json b/scripts/rewrite/oracle_seed_full_handlers.json index 8dfeecf..8aff69c 100644 --- a/scripts/rewrite/oracle_seed_full_handlers.json +++ b/scripts/rewrite/oracle_seed_full_handlers.json @@ -3412,6 +3412,38 @@ "oracle": "none", "source": "capstone-auto-discovery" }, + { + "name": "smoke_scasx_scasq", + "handler": "scasx", + "instruction_bytes": [ + 73, + 175 + ], + "initial": { + "registers": { + "RAX": "0x1122334455667788", + "RBX": "0x8877665544332211", + "RCX": "0x10", + "RDX": "0x2" + }, + "flags": { + "FLAG_CF": 0, + "FLAG_PF": 0, + "FLAG_AF": 0, + "FLAG_ZF": 0, + "FLAG_SF": 0, + "FLAG_OF": 0, + "FLAG_DF": 0, + "FLAG_IF": 1 + } + }, + "expected": { + "registers": {}, + "flags": {} + }, + "oracle": "none", + "source": "capstone-auto-discovery" + }, { "name": "smoke_setb_setb", "handler": "setb", diff --git a/scripts/rewrite/oracle_seed_full_handlers_enriched.json b/scripts/rewrite/oracle_seed_full_handlers_enriched.json index 60698db..0d87dcc 100644 --- a/scripts/rewrite/oracle_seed_full_handlers_enriched.json +++ b/scripts/rewrite/oracle_seed_full_handlers_enriched.json @@ -3786,6 +3786,40 @@ "oracle": "unicorn", "source": "capstone-auto-discovery" }, + { + "name": "smoke_scasx_scasq", + "handler": "scasx", + "instruction_bytes": [ + 73, + 175 + ], + "initial": { + "registers": { + "RAX": "0x1122334455667788", + "RBX": "0x8877665544332211", + "RCX": "0x10", + "RDX": "0x2" + }, + "flags": { + "FLAG_CF": 0, + "FLAG_PF": 0, + "FLAG_AF": 0, + "FLAG_ZF": 0, + "FLAG_SF": 0, + "FLAG_OF": 0, + "FLAG_DF": 0, + "FLAG_IF": 1 + } + }, + "expected": { + "registers": {}, + "flags": {} + }, + "oracle": "none", + "source": "capstone-auto-discovery", + "skip": true, + "skip_reason": "memory operand in 'scasq rax, qword ptr [rdi]'; needs mapped memory for emulation" + }, { "name": "smoke_setb_setb", "handler": "setb",