Two files (isl_report.py's docstring and structures/isl-builtins.md) recorded the same blocker on a faithful per-phase condition listing: that it needs the coroutine entry points from start_coroutine's operand. Measured against isl.call_sites(), which enumerates by scanning the encoding rather than by decoding and so is an independent denominator: linear + jumps, stopping at ret (what the tool did) 133 / 2846 = 4.7% linear + jumps, continuing past ret 2275 / 2846 = 79.9% ... + following start_coroutine (the recorded fix) 2355 / 2846 = 82.7% plain linear decode, no control flow at all 2846 / 2846 = 100.0% Following the coroutine entries buys 2.8 points. Disc-wide, a plain linear decode from the first phase base reaches 25705/25705 call sites over all 28 stages, and 28/28 decode clean to code_end with no desync. The real bug was isl.dis ending on `if op == 20: break`. Op 20 is `ret`, but this is a coroutine VM -- the thread suspends and resumes at the FOLLOWING instruction, so code continues past it. dis() now takes stop_at_ret (default True, preserving the old output: data/isl-stage02.txt regenerates byte-identical) and isl.linear_offsets() is the correct walk. By-product, kept with its control: start_coroutine's target is staged slot 0 -- 73/83 phase-1 sites land on a valid instruction, against a 38.7% chance rate for an arbitrary 4-aligned offset. New artefact data/isl-stage02-phase-ends.txt with a committed generator (isl_report.py phase-ends). It shows END_PHASE's call site is the WRONG place to read a clear condition: all 12 Stage-02 sites sit in one stereotyped outro. Not settled, and stated as such: op10/op13/op14/op21/op23 are unread handlers, so the condition in the poll loop upstream cannot be named yet.
105 lines
4.0 KiB
Python
105 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Regenerate the committed ISL artefacts under `docs/re/data/`.
|
|
|
|
isl_report.py <StageNN.ssb> calls -> the call-site census + a listing
|
|
|
|
The artefact was produced by an uncommitted one-off, so it drifted out of date
|
|
twice: once when operand staging was fixed (calls printed with too few
|
|
arguments) and once when three built-in names were corrected. Keeping the
|
|
generator in the tree is the point of this file.
|
|
|
|
isl_report.py <StageNN.ssb> phase-ends -> every END_PHASE with its context
|
|
|
|
The "needs the coroutine entry points" blocker recorded here is REFUTED: the
|
|
instruction stream is FLAT and `isl.linear_offsets` reaches 25705/25705 call
|
|
sites across all 28 stages. What broke the naive linear decode was `isl.dis`
|
|
stopping at op 20 (`ret`), which in a coroutine VM is a yield, not an end of
|
|
code -- see `docs/re/isl-stream-is-flat.md`.
|
|
"""
|
|
import collections
|
|
import sys
|
|
|
|
import isl
|
|
|
|
def census(b):
|
|
cs = isl.call_sites(b)
|
|
return cs, collections.Counter(bid for _, bid, _ in cs)
|
|
|
|
|
|
def emit_calls(b, path):
|
|
cs, h = census(b)
|
|
s1, s2 = isl.symbols(b, 1), isl.symbols(b, 2)
|
|
print('# %s — ISL disassembly artefacts' % path)
|
|
print()
|
|
print('Generated by `tools/re-capture/isl_report.py calls`.')
|
|
print()
|
|
print('%d call sites, %d distinct built-ins' % (len(cs), len(h)))
|
|
for bid, n in h.most_common():
|
|
print(' builtin %-4d %5d site(s)' % (bid, n))
|
|
print()
|
|
print('## phase-control sites')
|
|
for bid, nm in ((6, 'END_PHASE'), (62, 'FORCE_END_PHASE'),
|
|
(39, 'MARK_LAST_PHASE'), (40, 'mark_not_last')):
|
|
offs = [off for off, b2, _ in cs if b2 == bid]
|
|
print('%-3d %-18s %2d: %s'
|
|
% (bid, nm, len(offs), ' '.join('0x%x' % o for o in offs)))
|
|
print()
|
|
print('## named built-ins used, by traffic')
|
|
for bid, n in h.most_common():
|
|
nm = isl.BUILTIN.get(bid)
|
|
if nm:
|
|
print(' %-3d %-24s %4d' % (bid, nm, n))
|
|
print()
|
|
print('## phase code bases')
|
|
print('Each phase has its OWN base; the file header offset is not it.')
|
|
print(' ' + ' '.join('0x%x' % x for x in isl.phase_bases(b)))
|
|
print()
|
|
print('## disassembly into the first END_PHASE')
|
|
target = [off for off, bid, _ in cs if bid == 6][0]
|
|
start = isl.resync(b, target)
|
|
print('resync from 0x%X' % start)
|
|
for line in isl.dis(b, start, 64, code_base=0x24, sym2=s2, sym1=s1):
|
|
print(line)
|
|
|
|
|
|
def emit_phase_ends(b, path):
|
|
"""Every phase-ending call with the instructions that lead into it."""
|
|
offs = isl.linear_offsets(b)
|
|
idx = {o: k for k, o in enumerate(offs)}
|
|
bases = isl.phase_bases(b)
|
|
s1, s2 = isl.symbols(b, 1), isl.symbols(b, 2)
|
|
ends = [(o, bid) for o, bid, _ in isl.call_sites(b) if bid in (6, 62)]
|
|
print('# %s — where each phase ends' % path)
|
|
print()
|
|
print('Generated by `tools/re-capture/isl_report.py phase-ends`.')
|
|
print('Decoded with `stop_at_ret=False`; a `ret` is a coroutine YIELD, so')
|
|
print('the listing continues past it.')
|
|
print()
|
|
print('phase code bases: ' + ' '.join('0x%x' % x for x in bases))
|
|
print('%d phase-ending call(s): %d END_PHASE, %d FORCE_END_PHASE'
|
|
% (len(ends), sum(1 for _, b2 in ends if b2 == 6),
|
|
sum(1 for _, b2 in ends if b2 == 62)))
|
|
for o, bid in ends:
|
|
ph = sum(1 for x in bases if x <= o)
|
|
k = idx.get(o)
|
|
if k is None:
|
|
print('\n## 0x%X builtin %d — NOT on the linear stream' % (o, bid))
|
|
continue
|
|
print('\n## phase %d — builtin %d (%s) at 0x%X'
|
|
% (ph, bid, isl.BUILTIN.get(bid, '?'), o))
|
|
for line in isl.dis(b, offs[max(0, k - 16)], 22,
|
|
code_base=bases[ph - 1], sym2=s2, sym1=s1,
|
|
stop_at_ret=False):
|
|
print(' ' + line)
|
|
|
|
|
|
def main():
|
|
path = sys.argv[1]
|
|
b = isl.load(path)
|
|
name = path.replace('\\', '/').split('/')[-1]
|
|
{'calls': emit_calls, 'phase-ends': emit_phase_ends}[sys.argv[2]](b, name)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|