2026-09-02
- (/loop) The differential calls back into guest code, and
qsort agrees
differential cases 63 -> 76 (76 rebuilt, 76 agreed, 0 diverging)
tests 1947 -> 1950
Thirteen cases across qsort and bsearch -
the first where the reference and orbistoun have to agree about
calling back into the caller's own code, not merely
about a return value.
Why these were the interesting ones left
Every case so far handed orbistoun data and compared what came back. A comparison-sorted array is different: orbistoun has to invoke a function the caller supplied, in the guest's calling convention, once per comparison, and get the sense of the answer right.
It turns out to be straightforward for the reason the whole project
rests on: guest code runs natively in this process.
GuestComparator is
extern "sysv64" fn(u64, u64) -> u64 reached by
transmuting an address, so a test writes exactly that function and hands
over its address. There is no bridge to build - the emulator calls it as
it would call a guest's.
Cases: jumbled, already sorted, reversed, duplicates, a single
element, negatives; and for bsearch the first, middle and
last elements, three kinds of absent, and an empty array. All
thirteen agree.
qsort/jumbled passing is the proof the callback
ran at all. A qsort that did nothing would still
match already-sorted, which is exactly the sort of false
pass a differential has to be built to avoid - so the jumbled case is
doing double duty as the liveness check.
Two things the record format needed
A byte blob is not text. A four-byte 5
is 05 00 00 00, so element data cannot ride in a
NUL-terminated field at all. b: carries plain hex, and an
odd digit count is refused rather than rounded - half a
byte means the record was truncated, and a case built from a truncated
array would be compared against the wrong input while looking like it
worked.
A comparator is code, and that is the one place the "one case
list" property does not reach. The reference cannot hand its
machine code to the checker, so the case names a semantic -
int32-asc - and each side implements it. A name the checker
does not know answers None, which the rebuild gate reports
rather than skipping: an unrecognised comparator must not become a
silently absent case. That is stated in both files, because it is the
seam where the two sides could drift and nothing else would notice.
State
cargo test --workspace green - 117 suites, 1950
tests, 0 failures. clippy --tests clean, fmt
clean, identity scan clean. also differential reports
76 cases, matching tools/differential/reference.c.
The agreement count was re-checked by deliberately breaking it: 76 rebuilt, 76 agreed, 0 diverging.
Nothing committed. The day holds worklogs 292-322 and D466-D479.
Next: strtok, whose answer depends on
state carried between calls - a single case cannot express it, so the
record format needs a sequence, and that wants designing before it is
written. Then the sign-extended return width, which wants a decision
before a sweep: nine measurements say the console extends and orbistoun
does not, and whether that is per function or the whole vendor family is
the question to settle first.