score.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. """MCD proc-compare scorer. Usage: score.py ORIG_TXT OURS_TXT MOD PAIRS...
  2. PAIRS = OrigName:OursName. Masks absolute targets, proc refs, dep slots;
  3. leave-truncates bodies; reports SAME/PREFIX/DIFF per pair."""
  4. import re, sys
  5. def procs(path, mod):
  6. txt = open(path, errors='replace').read()
  7. heads = [(m.group(1), int(m.group(2), 16))
  8. for m in re.finditer(r'(?m)^;? ?' + mod + r'\.(\w+)\s*: ([0-9a-f]{4})$', txt)]
  9. ops = []
  10. cur = None
  11. for line in txt.splitlines():
  12. m = re.match(r'^\s*([0-9a-f]{4})\s+((?:[0-9a-f]{2}\s*)+?)(?:\s{2,}|\t+|$)(.*)$', line)
  13. if m:
  14. cur = int(m.group(1), 16)
  15. ops.append((cur, m.group(2).split(), m.group(3).strip()))
  16. continue
  17. # case-table lines carry no address: attach to preceding cd op so
  18. # CASE bounds/labels participate in comparison (else invisible).
  19. # NOTE: unassemble prints labels +90 (subtracts 32678 not 32768);
  20. # the shift applies equally to both sides, so comparison stays valid.
  21. c = re.match(r'^\s*case (\d+) => ([0-9a-f]{4})$', line)
  22. if c and cur is not None:
  23. ops.append((cur, ['cd'], 'case %d => %s' % (int(c.group(1)), c.group(2))))
  24. out = {}
  25. addrs = sorted(set(a for _, a in heads))
  26. for name, a in heads:
  27. if name in out:
  28. continue
  29. nxt = min([x for x in addrs if x > a] or [0x10000])
  30. out[name] = [(o, b, m) for (o, b, m) in ops if a <= o < nxt and b != ['00'] and m]
  31. return out
  32. def norm(m):
  33. m = re.sub(r'\b[0-9a-f]{4}\b', '@@@@', m)
  34. m = re.sub(r'call_with_frame proc\d+', 'call_with_frame proc##', m)
  35. m = re.sub(r'nested_call proc\d+', 'nested_call proc##', m)
  36. m = re.sub(r'(?<!_)proc\d+', 'proc##', m)
  37. return m
  38. def trunc(ms):
  39. li = [i for i, m in enumerate(ms)
  40. if m.split()[0] in ('leave', 'leave0', 'leave2', 'leave4', 'leave6',
  41. 'fct_leave', 'longfct_leave', 'end_program', 'Raise')]
  42. return ms[:li[-1] + 1] if li else ms
  43. def main():
  44. orig_txt, ours_txt, mod = sys.argv[1], sys.argv[2], sys.argv[3]
  45. A, B = procs(orig_txt, mod), procs(ours_txt, mod)
  46. same = tot = 0
  47. for pair in sys.argv[4:]:
  48. o, u = pair.split(':')
  49. tot += 1
  50. if o not in A:
  51. print(f'{u}/{o} ORIG-MISSING'); continue
  52. if u not in B:
  53. print(f'{u}/{o} OURS-MISSING'); continue
  54. xa, xb = trunc([norm(m) for (_, _, m) in A[o]]), trunc([norm(m) for (_, _, m) in B[u]])
  55. n = min(len(xa), len(xb))
  56. if xa == xb:
  57. same += 1
  58. print(f'{u}/{o} {len(xa)} vs {len(xb)} SAME')
  59. elif xa[:n] == xb[:n]:
  60. print(f'{u}/{o} {len(xa)} vs {len(xb)} PREFIX(+{abs(len(xa)-len(xb))})')
  61. else:
  62. print(f'{u}/{o} {len(xa)} vs {len(xb)} DIFF')
  63. print(f'{same}/{tot} exact')
  64. main()