[Docs] (P13 W5 S2): the guard classifier's transport words and the rewrite tool.

This commit is contained in:
2026-10-06 10:51:09 -04:00
parent a6c02a1741
commit 0fbcaed454
2 changed files with 97 additions and 1 deletions
@@ -37,7 +37,9 @@ TRANSPORT = [r'ClientSession', r'ServerSession', r'ServerLoop', r'Transport::',
r'Transport\s*!=', r'Transport\s*==',
# Second pass (S2): transport-shaped words that name no MG_Remote symbol.
r'Window', r'SwapInterval', r'ContextLive', r'Ahb', r'AHardwareBuffer', r'HardwareBuffer', r'[Dd]ma[Bb]uf',
r'Gbm', r'SyncFile', r'sync_file', r'Session', r'Peer', r'Spawn', r'Tcp', r'Socket']
r'Gbm', r'SyncFile', r'sync_file', r'Session', r'Peer', r'Spawn', r'Tcp', r'Socket',
# Device loss and its recovery are a session's (the session ends, not the process).
r'Lost', r'Epoch', r'ResetNotif', r'Recover', r'StreamGate', r'ReplacementShare', r'ReplaceProcessDefault']
RECORD = [r'DataArmIsRecord', r'RecordArm', r'Staged', r'\bWire', r'MGPipe', r'Handle', r'Record\b', r'Twin',
r'Respecif', r'Readback', r'Verb', r'PersistentMap', r'GpuWrite', r'Emit', r'Applier', r'Slot',
r'Residual', r'ContextValues', r'Cso\b', r'CSO', r'Placeholder', r'Mipmap', r'Shadow']
@@ -0,0 +1,94 @@
#!/usr/bin/env python3
"""P13 W5 S2: move the record arm's guards from MOBILEGL_BUILD_DISAGGREGATED to
MOBILEGL_BUILD_RECORD_ARM, by w5_classify.py's classes.
w5_rewrite.py [--root DIR] [--dir PREFIX ...] [--dry-run]
Rewritten (the macro on the opening line, and on its #else / #endif comment if it names it):
* a plain `#if MOBILEGL_BUILD_DISAGGREGATED` group of class A or F (record terms or nothing, no
transport word, no MG_Remote name);
* every plain `#if !MOBILEGL_BUILD_DISAGGREGATED` (the frontend arm of a build without the record arm);
* `#if MOBILEGL_PIPE_VERIFY && !MOBILEGL_BUILD_DISAGGREGATED` (the verify tree's frontend pins).
Left for the hand pass (S3): B, C, D, E and the compound guards; the MOBILEGL_PIPE_POISON arm in
PipeInputs.h stays on DISAGG on purpose (the FCL library must not pay a per-read poison check).
Tests (MG_Test, MG_IntegrationTest) are S6's.
While MOBILEGL_BUILD_RECORD_ARM equals MOBILEGL_BUILD_DISAGGREGATED (until S7) every rewrite is a
no-op for the compiler: both trees keep byte-identical code.
"""
import argparse
import pathlib
import re
import sys
sys.path.insert(0, str(pathlib.Path(__file__).parent))
import w5_classify as wc # noqa: E402
OLD = 'MOBILEGL_BUILD_DISAGGREGATED'
NEW = 'MOBILEGL_BUILD_RECORD_ARM'
SKIP_TOP = ('MobileGL/MG_Test/', 'MobileGL/MG_IntegrationTest/')
# S2 touches only the trees the record core lives in. The EGL layer, the GL context's device-loss
# state, the logger's role files and the stats dump paths are session machinery whose guards the
# token classes cannot tell apart from record code; they are S3's hand pass.
RECORD_DIRS = ('MobileGL/MG_Backend/DirectGLES/', 'MobileGL/MG_Backend/DirectVulkan/', 'MobileGL/MG_Backend/MGPipe/',
'MobileGL/MG_Pipe/', 'MobileGL/MG_Impl/Pipe/', 'MobileGL/MG_Impl/GLImpl/',
'MobileGL/MG_State/GLState/BufferState/', 'MobileGL/MG_State/GLState/ProgramState/',
'MobileGL/MG_State/GLState/FramebufferState/', 'MobileGL/MG_Util/SelfTest/')
def rewrite_file(path, rel, lines):
rows = wc.classify(rel, lines)
changed = []
for g in wc.groups(lines):
cond = g['cond']
plain = re.fullmatch(r'if\s+' + OLD, cond) is not None
negated = re.fullmatch(r'if\s+!\s*' + OLD, cond) is not None
verify_pin = re.fullmatch(r'if\s+MOBILEGL_PIPE_VERIFY\s*&&\s*!\s*' + OLD, cond) is not None
if plain:
cls = next(r['class'] for r in rows if r['open'] == g['open'])
if cls not in ('A', 'F'):
continue
elif not (negated or verify_pin):
continue
targets = [g['open']] + [no for no, kind in g['branches'][1:]] + [g['close']]
for no in targets:
line = lines[no - 1]
if no == g['open'] or OLD in line:
lines[no - 1] = line.replace(OLD, NEW)
changed.append((g['open'], 'plain' if plain else 'negated' if negated else 'verify-pin'))
return changed
def main():
ap = argparse.ArgumentParser()
ap.add_argument('--root', default='.')
ap.add_argument('--dir', action='append', default=[])
ap.add_argument('--dry-run', action='store_true')
args = ap.parse_args()
root = pathlib.Path(args.root)
total = 0
for p in sorted((root / 'MobileGL').rglob('*')):
if p.suffix not in wc.EXTS or not p.is_file():
continue
rel = p.relative_to(root).as_posix()
if wc.SKIP_PARTS & set(pathlib.PurePosixPath(rel).parts) or rel.startswith(SKIP_TOP) or not rel.startswith(RECORD_DIRS):
continue
if args.dir and not any(rel.startswith(d) for d in args.dir):
continue
raw = p.read_bytes().decode('utf-8')
crlf = '\r\n' in raw
lines = raw.replace('\r\n', '\n').splitlines(keepends=True)
changed = rewrite_file(p, rel, lines)
if not changed:
continue
total += len(changed)
print('%-70s %3d' % (rel, len(changed)))
if not args.dry_run:
text = ''.join(lines)
p.write_bytes((text.replace('\n', '\r\n') if crlf else text).encode('utf-8'))
print('groups rewritten:', total)
return 0
if __name__ == '__main__':
sys.exit(main())