feat: SCJUMP guarded-DFS decode -> decision list (Task 2)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -59,6 +59,89 @@ def chapter_dispatch(scr) -> dict:
|
||||
return out
|
||||
|
||||
|
||||
FLIP = {"==": "==", "!=": "!=", "<": ">", "<=": ">=", ">": "<", ">=": "<="}
|
||||
NEG = {"==": "!=", "!=": "==", "<": ">=", ">=": "<", "<=": ">", ">": "<="}
|
||||
|
||||
|
||||
def _cmp_expr(op_sym, a, b):
|
||||
"""Build a Cmp guard from a comparison's operands (a <op> b). Global vs immediate only;
|
||||
anything else -> opaque."""
|
||||
if a[0] in GLOBAL_ATYPES and b[0] == IMM:
|
||||
return {"global": a[1], "op": op_sym, "value": b[1]}
|
||||
if a[0] == IMM and b[0] in GLOBAL_ATYPES:
|
||||
return {"global": b[1], "op": FLIP[op_sym], "value": a[1]}
|
||||
return {"opaque": f"cmp {op_sym} {a} {b}"}
|
||||
|
||||
|
||||
def negate(g):
|
||||
if "opaque" in g: return {"opaque": "!(" + g["opaque"] + ")"}
|
||||
if "and" in g: return {"or": [negate(x) for x in g["and"]]}
|
||||
if "or" in g: return {"and": [negate(x) for x in g["or"]]}
|
||||
return {"global": g["global"], "op": NEG[g["op"]], "value": g["value"]}
|
||||
|
||||
|
||||
def _resolve(localmap, operand):
|
||||
"""Resolve a jcc/logic operand (a local holding a condition) to its guard expression."""
|
||||
return localmap.get(operand[1], {"opaque": f"local {operand}"})
|
||||
|
||||
|
||||
def chapter_of(guards):
|
||||
for g in guards:
|
||||
if g.get("global") == CHAPTER_GLOBAL and g.get("op") == "==":
|
||||
return g["value"]
|
||||
return None
|
||||
|
||||
|
||||
def decode(scr):
|
||||
code = scr.instructions
|
||||
by_off = {ins.offset: i for i, ins in enumerate(code)}
|
||||
decisions = []
|
||||
# explicit-stack DFS: each item is (idx, localmap, guards). No back-edges -> terminates.
|
||||
stack = [(0, {}, [])]
|
||||
while stack:
|
||||
idx, localmap, guards = stack.pop()
|
||||
localmap = dict(localmap)
|
||||
while 0 <= idx < len(code):
|
||||
ins = code[idx]
|
||||
op, a = ins.opcode, ins.args
|
||||
if ins.offset == END_TARGET:
|
||||
break
|
||||
if op == MOV and a and tuple(a[0]) == (3, DECISION_GLOBAL) and a[1][0] == IMM:
|
||||
decisions.append({"site_offset": ins.offset, "chapter": chapter_of(guards),
|
||||
"decision": a[1][1], "guards": list(guards)})
|
||||
idx += 1
|
||||
continue
|
||||
if op in CMP_OPS and len(a) >= 3 and a[0][0] == 9:
|
||||
localmap[a[0][1]] = _cmp_expr(CMP_OPS[op], a[1], a[2])
|
||||
idx += 1
|
||||
continue
|
||||
if op in LOGIC_OPS and len(a) >= 3 and a[0][0] == 9:
|
||||
kind = LOGIC_OPS[op]
|
||||
localmap[a[0][1]] = {kind: [_resolve(localmap, a[1]), _resolve(localmap, a[2])]}
|
||||
idx += 1
|
||||
continue
|
||||
if op == JCC and len(a) >= 3:
|
||||
expr = _resolve(localmap, a[0])
|
||||
a_tgt, b_tgt = a[1][1], a[2][1]
|
||||
true_idx = idx + 1 if a_tgt == FALLTHROUGH else by_off.get(a_tgt)
|
||||
false_idx = idx + 1 if b_tgt == FALLTHROUGH else by_off.get(b_tgt)
|
||||
# explore false branch later; continue true branch inline
|
||||
if false_idx is not None:
|
||||
stack.append((false_idx, dict(localmap), guards + [negate(expr)]))
|
||||
if true_idx is None:
|
||||
break
|
||||
idx, guards = true_idx, guards + [expr]
|
||||
continue
|
||||
if op == JMP and a:
|
||||
tgt = a[0][1]
|
||||
if tgt == END_TARGET or tgt not in by_off:
|
||||
break
|
||||
idx = by_off[tgt]
|
||||
continue
|
||||
idx += 1
|
||||
return decisions
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--verify", action="store_true") # implemented in Task 4
|
||||
|
||||
@@ -16,7 +16,21 @@ def test_cfg_acyclic_and_dispatch():
|
||||
expected = {1:0x81, 2:0x9f, 3:0x1ded, 4:0x1e6d, 5:0x54c1, 6:0x93c4, 7:0x1e868, 8:0x2aa42, 9:0x2b8a3}
|
||||
check(disp == expected, f"chapter dispatch matches known offsets (got {disp})")
|
||||
|
||||
def test_decode_anchor_and_count():
|
||||
scr = S.load_scjump()
|
||||
decs = S.decode(scr)
|
||||
check(len({d["site_offset"] for d in decs}) == 1755, f"1755 distinct decision sites (got {len({d['site_offset'] for d in decs})})")
|
||||
# anchor: chapter 1, decision 0, guarded by 0x6d3 != 1
|
||||
anchor = [d for d in decs if d["chapter"] == 1 and d["decision"] == 0]
|
||||
check(len(anchor) >= 1, "chapter-1 decision 0 exists")
|
||||
a = anchor[0]
|
||||
has_chapter = any(g.get("global") == 0x3234 and g.get("op") == "==" and g.get("value") == 1 for g in a["guards"])
|
||||
has_flag = any(g.get("global") == 0x6d3 and g.get("op") == "!=" and g.get("value") == 1 for g in a["guards"])
|
||||
check(has_chapter, "anchor guarded by chapter_mode==1")
|
||||
check(has_flag, "anchor guarded by 0x6d3!=1")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_cfg_acyclic_and_dispatch()
|
||||
test_decode_anchor_and_count()
|
||||
print(f"\n{len(FAILS)} failures")
|
||||
sys.exit(1 if FAILS else 0)
|
||||
|
||||
Reference in New Issue
Block a user