phase12: merge 22 (665 bodies) + the pool band filter, and I had the first call backwards
MERGE 22: +2 bodies, both from worker D's un-attempted band. 0x801092C0 (52 B) and 0x80085B44 (60 B), both re-verified by me from fresh --work dirs first. Gate: c_regions=674, differing_bytes=0, MATCH. POOL FILTER, SECOND LEAK, MY ERROR CORRECTED. D found 0x80012D54 -- the charter-blocked 0x80012xxx primitive-init family, with class , so the address-parsing rule I installed after the FIRST leak (0x80012A98) had no text to match on. D read its bytes and confirmed the family. I had explicitly DECLINED band-blocking the first time, arguing it 'would silently discard unclassified rows on an inference the charter does not make'. That was backwards: the charter names the family as 'the 0x80012xxx primitive-init family', i.e. BY ADDRESS BAND, and earlier phases already acted on it -- 0x80012A10/AE0/B20/CFC, all class '-', are excluded BY NAME in the Makefile. Two rows leaked by label phrasing and the second had no label at all. The band is the predicate. Blocked 8 -> 15, pools 70 -> 54. Also applying my own corrected commit idiom: this commit names the two claim sources rather than using 'git add -A src/', which swept three of D's in-flight rows into an earlier commit. New levers recorded: LOAD/STORE INTERLEAVING NEEDS BOTH SIDES VOLATILE (a volatile load can still hoist above a plain store and vice versa; cc1 orders volatile only against volatile) -- and from 0x801092C0, unsigned-parameter signedness read from the instruction AFTER the test, volatile on a table pointer for an alias reason, cookbook 19 generalising to a forward loop, and `p += i + start; p->p0 = 0;` over the subscript form.
This commit is contained in:
@@ -377,6 +377,7 @@
|
||||
0x800834B8 0x80083504 src/func_800834B8.c
|
||||
0x80083504 0x8008352C src/func_80083504.c
|
||||
0x8008352C 0x8008355C src/func_8008352C.c
|
||||
0x80085B44 0x80085B80 src/func_80085B44.c
|
||||
0x80085B80 0x80085B90 src/func_80085B80.c
|
||||
0x80089314 0x80089338 src/func_80089314.c
|
||||
0x800893E8 0x80089434 src/func_800893E8.c
|
||||
@@ -668,6 +669,7 @@
|
||||
0x80108710 0x80108734 src/func_80108710.c
|
||||
0x80108740 0x801087C8 src/func_80108740.c maspsx=epilogue
|
||||
0x80108990 0x801089B4 src/func_80108990.c
|
||||
0x801092C0 0x801092F4 src/func_801092C0.c
|
||||
0x80109300 0x80109314 src/func_80109300.c
|
||||
0x80109314 0x80109338 src/func_800F8F9C.c
|
||||
0x80109778 0x80109790 src/func_80109778.c
|
||||
|
||||
|
@@ -1049,3 +1049,51 @@ instruction count, and from the allocator class, which permutes registers — th
|
||||
A spelling that moved the base's assignment after the two calls put the `la` exactly where the original
|
||||
has it and changed neither the hoist nor the fourth save, which is what makes it a class rather than a
|
||||
missed spelling.
|
||||
|
||||
### The pool filter's SECOND leak, and my first call was backwards (worker D)
|
||||
|
||||
Worker D found `0x80012D54` in its un-attempted band: **the `0x80012xxx` primitive-init family, which
|
||||
charter section 7 blocks, and with class `-`** — so the address-parsing rule I installed after the
|
||||
FIRST leak (`0x80012A98`, which at least names its siblings by address) had no text to match on. D read
|
||||
the bytes and confirmed the family: `li v0,4 / sh v0,6(a0) / lui v0,0x400 / sw v0,8(a0) /
|
||||
lui v0,0x5000 / sw zero,0(a0)`.
|
||||
|
||||
**I had explicitly declined band-blocking the first time, and the reasoning was backwards.** I argued
|
||||
that blocking the whole `0x80012xxx` band "would silently discard unclassified rows on an inference the
|
||||
charter does not make". But **the charter makes exactly that inference — it names the family as "the
|
||||
`0x80012xxx` primitive-init family", i.e. BY ADDRESS BAND** — and earlier phases had already acted on
|
||||
it, since `0x80012A10`, `0x80012AE0`, `0x80012B20` and `0x80012CFC`, all with class `-`, are excluded
|
||||
**by name** in the Makefile. Two rows leaked by label phrasing, the second with no label at all, and
|
||||
that is the evidence that settled it: **the band is the predicate.** Blocked rows still report their
|
||||
reason. The blocked count went 8 -> 15 and the pools 70 -> **54** rows.
|
||||
|
||||
Recorded as a coordinator error with its correction, because the first fix looked reasonable and was
|
||||
the reason the second row survived.
|
||||
|
||||
### Worker D's un-attempted band is producing bodies, and it was the right call to build it
|
||||
|
||||
D took the band I generated from its own criterion (rows with no mechanism named, never pushed to a
|
||||
floor) and closed **2 of 4 rows attempted**, both at 4-6 spellings, and **both correct-LENGTH
|
||||
candidates** — so the band's rows are genuinely structural rather than misidentified. D's own summary
|
||||
is the one to remember: *the failures are allocator-shaped, not "wrong function"-shaped.*
|
||||
|
||||
**`0x80085B44` (60 B) gave the phase a sharp new lever: LOAD/STORE INTERLEAVING NEEDS BOTH SIDES
|
||||
VOLATILE.** Plain C batches (`lw/lw/lw` then three stores, 25 differing bytes); `volatile int *src`
|
||||
alone is still 25 bytes, **because a volatile LOAD can still hoist above a plain store**; `volatile`
|
||||
globals alone is still 25 bytes, **because a volatile STORE does not stop a plain load hoisting above
|
||||
it**; both together match. **cc1 orders volatile accesses only against other volatile accesses.** That
|
||||
is a precise statement of a boundary that four workers have been circling all phase.
|
||||
|
||||
**`0x801092C0` (52 B) carried four levers, three new:**
|
||||
* **UNSIGNED parameters** — `int` gives `blez`/signed `slt`, `unsigned` gives the original's `beqz`
|
||||
AND `sltu`. **The entry test alone cannot tell you; the instruction AFTER it names the signedness.**
|
||||
* **`volatile` on the table POINTER for an alias reason** — with a plain `Rec *` global cc1 hoists the
|
||||
pointer load out of the loop, because the store's type (a pointer field of the record) is provably
|
||||
not the global's own type; the original reloads every iteration. A proof by emission, not a taste.
|
||||
* **Cookbook 19 generalises to a FORWARD loop** — the unused `addiu sp,sp,-8 / addiu sp,sp,8` pair
|
||||
appears for `for (i=0;i<n;i++)` (64 B) and disappears with `i=0; if (n) do {...} while (++i<n);`
|
||||
(52 B). Cookbook 19 recorded it for a BACKWARD loop and warned it did not generalise; this is the
|
||||
second instance, so its limit tightens to "loop-shape artifact" generally.
|
||||
* **`p += i + start; p->p0 = 0;` rather than `p[i + start].p0 = 0;`** — the subscript form writes the
|
||||
address sum into the OFFSET's register and rotates the loop; accumulating into a pointer makes the
|
||||
sum's destination the BASE register (the original's `addu v1,v1,v0`).
|
||||
|
||||
+22
-18
@@ -151,14 +151,27 @@ BLOCKED_CLASS_SUBSTRINGS = ("trapping-arithmetic", "primitive-init")
|
||||
# -- it names the blocked family **by address**, so the keyword substring test passes it straight
|
||||
# through to prio 1. This is the same "half-right class string" pattern the workers kept hitting.
|
||||
#
|
||||
# The fix is to read the ADDRESSES out of the class text and block the row if any of them is itself
|
||||
# excluded. That is deliberately preferred over blocking the whole `0x80012xxx` band, which was the
|
||||
# other option D offered, **and the choice is measured**: the band holds 9 index rows and only TWO
|
||||
# are the family (`0x80012A48` by keyword, `0x80012A98` by this rule). The other seven record class
|
||||
# `-`, i.e. merely unclassified -- four of them are excluded by name in the Makefile anyway, and
|
||||
# band-blocking would silently discard the remaining three on an inference the charter does not make.
|
||||
# Rows that reference a blocked family are reported, not guessed at.
|
||||
# First fix: read the ADDRESSES out of the class text and block the row if any of them is itself
|
||||
# excluded. That catches `0x80012A98`, which names its family as "same family as
|
||||
# 0x80012A10/0x80012AE0".
|
||||
#
|
||||
# **THAT WAS NOT ENOUGH, AND WORKER D FOUND THE SECOND LEAK: `0x80012D54`, whose class is `-`.**
|
||||
# There is no class text to parse, so no text rule can ever catch it -- and D read its BYTES and
|
||||
# confirmed it IS the family: `li v0,4 / sh v0,6(a0) / lui v0,0x400 / sw v0,8(a0) / lui v0,0x5000 /
|
||||
# sw zero,0(a0)`.
|
||||
#
|
||||
# So the band itself is blocked, which is what the CHARTER actually says: it names the family as
|
||||
# **"the `0x80012xxx` primitive-init family"**, i.e. BY ADDRESS BAND. I first declined this because
|
||||
# band-blocking would discard unclassified rows "on an inference the charter does not make" -- and
|
||||
# that was backwards: the charter makes exactly that inference, and it had already been acted on,
|
||||
# since `0x80012A10`, `0x80012AE0`, `0x80012B20` and `0x80012CFC` (all class `-`) are excluded BY NAME
|
||||
# in the Makefile. **Two workers found the leak twice and the second instance had no text to match on,
|
||||
# which is the evidence that settled it.** Blocked rows are still REPORTED with their reason.
|
||||
FAMILY_ADDRESS_RE = re.compile(r"0[xX][0-9A-Fa-f]{6,8}")
|
||||
BLOCKED_ADDRESS_BANDS = (
|
||||
# The charter's own wording. See the comment above for why the band, not a string.
|
||||
(0x80012000, 0x80013000, "charter s7: the `0x80012xxx` primitive-init family"),
|
||||
)
|
||||
|
||||
# *** THE TRIAGE KEY WAS IMPLEMENTED, TESTED AGAINST THE PHASE'S OWN RECORD, AND IT FAILED ***
|
||||
#
|
||||
@@ -427,9 +440,6 @@ def main():
|
||||
dropped_done = []
|
||||
dropped_blocked = []
|
||||
dropped_own = []
|
||||
# Rows in a charter-named FAMILY band that the index never classified. Not blocked on an
|
||||
# inference, but reported, so a human can decide rather than having it decided silently.
|
||||
unclassified_family_band = []
|
||||
for addr, size, status, klass in unregistered:
|
||||
bucket = names[zlib.crc32(f"{addr:#010x}".encode()) % args.workers]
|
||||
# CHARTER SECTION 7. A blocked class must not be attempted at all, so it must not be
|
||||
@@ -438,7 +448,9 @@ def main():
|
||||
blob = f"{status} {klass}".lower()
|
||||
# A family reference BY ADDRESS counts exactly like a keyword. D's `0x80012A98` case.
|
||||
family_refs = {int(x, 16) for x in FAMILY_ADDRESS_RE.findall(klass or "")}
|
||||
band = next((reason for lo, hi, reason in BLOCKED_ADDRESS_BANDS if lo <= addr < hi), None)
|
||||
if ("blocked" in status
|
||||
or band is not None
|
||||
or any(s in blob for s in BLOCKED_CLASS_SUBSTRINGS)
|
||||
or (family_refs & (excluded | blocked_addrs))):
|
||||
dropped_blocked.append((addr, bucket, status, klass))
|
||||
@@ -469,8 +481,6 @@ def main():
|
||||
prio = min(prio, 2)
|
||||
if "deferred" in status:
|
||||
prio = 4
|
||||
if 0x80012000 <= addr < 0x80013000 and not (klass or "").strip().strip("-"):
|
||||
unclassified_family_band.append((addr, bucket))
|
||||
# A NOTE column, because free-text staging cannot be parsed reliably enough to decide
|
||||
# "done" vs "cheap retry" in every case. Where the tool CAN decide it drops the row (the
|
||||
# absence-token case, above); where it cannot, it says who classified it and lets the worker
|
||||
@@ -533,12 +543,6 @@ def main():
|
||||
if len(dropped_done) > 5:
|
||||
print(f" ... and {len(dropped_done) - 5} more")
|
||||
print(f" dropped, this bucket's OWN prior classification {len(dropped_own)}")
|
||||
if unclassified_family_band:
|
||||
print(f" NOT blocked but FLAGGED: {len(unclassified_family_band)} row(s) inside a"
|
||||
f" charter-named family band that the index never classified:")
|
||||
for addr, bucket in sorted(unclassified_family_band):
|
||||
print(f" {addr:#010x} (pool {bucket}) -- class '-', so the charter's family"
|
||||
f" membership is an INFERENCE, not a record. Decide deliberately.")
|
||||
print(f"pool rows written {sum(written.values())}"
|
||||
f" (check: {len(unregistered) - len(leaked) - len(dropped_done) - len(dropped_blocked) - len(dropped_own)})")
|
||||
for n in names:
|
||||
|
||||
@@ -278,15 +278,17 @@ class TestSyntheticPools(unittest.TestCase):
|
||||
self.assertEqual(self.pool_rows(), [],
|
||||
"a class naming an EXCLUDED address is a family reference, not a pass")
|
||||
|
||||
def test_a_family_band_row_with_no_class_is_flagged_not_blocked(self):
|
||||
"""The other option D offered was to block the whole `0x80012xxx` band. Measured: the band
|
||||
holds 9 index rows and only TWO are the family. Blocking by prefix would silently discard
|
||||
unclassified rows on an inference the charter does not make, so they are KEPT and reported."""
|
||||
def test_a_family_band_row_with_NO_CLASS_is_blocked(self):
|
||||
"""I first kept these rows, arguing that blocking by band discards unclassified rows on an
|
||||
inference the charter does not make. Worker D then produced `0x80012D54`: class `-`, no text
|
||||
to match on, and its BYTES confirm it IS the family. The charter names the family BY BAND
|
||||
("the `0x80012xxx` primitive-init family"), and earlier phases already excluded four class-`-`
|
||||
band rows by name -- so the band is the right predicate and my first call was backwards."""
|
||||
self.write_negatives([("0x8001278C", "-", "near-match")])
|
||||
r = self.run_tool()
|
||||
self.assertEqual(r.returncode, 0, r.stderr)
|
||||
self.assertEqual(len(self.pool_rows()), 1,
|
||||
"an unclassified band row is not blocked, only flagged")
|
||||
self.assertEqual(self.pool_rows(), [],
|
||||
"the charter names the family by BAND, and class `-` cannot be matched on")
|
||||
|
||||
def test_rare_epilogue_order_is_NOT_blocked(self):
|
||||
"""Worker A's distinction: the epilogue ORDER is work (cookbook 140/147/165 shipped
|
||||
|
||||
Reference in New Issue
Block a user