import struct, collections, isl, isl_cfg def edges(b, spawn=False): """off -> successors. `spawn` includes the coroutine a start_coroutine creates.""" offs = isl.linear_offsets(b) nxt = {offs[i]: offs[i+1] for i in range(len(offs)-1)} bases = isl.phase_bases(b) E = collections.defaultdict(list) loc, sp = {}, {} for off in offs: w = struct.unpack_from('>I', b, off)[0] op, ln = w & 0xFF, (w >> 8) & 0xFF sk, dk = (w >> 24) & 0xFF, (w >> 16) & 0xFF words = [struct.unpack_from('>I', b, off+i)[0] for i in range(4, max(ln,4), 4) if off+i+4 <= len(b)] ph = sum(1 for x in bases if x <= off) base = bases[ph-1] if ph else bases[0] fall = nxt.get(off) if op == 0 and len(words) >= 2: v = words[1] if sk == 1 else sp.get(words[1]) if sk == 2 else None d = sp if dk == 2 else loc if v is None: d.pop(words[0], None) else: d[words[0]] = v elif op == 19 and words: if words[0] == 11: fall = None # end_coroutine: thread dies if spawn and words[0] == 1 and 0 in loc: t = base + loc[0] if t in nxt or t in offs: E[off].append(t) loc = {} elif op == 12 and words: E[off].append(base + words[0]); fall = None elif op in isl.REL and words: E[off].append(base + words[0]) if fall: E[off].append(fall) return E, nxt def guards(b, spawn=False): offs = isl.linear_offsets(b) E, nxt = edges(b, spawn) rev = collections.defaultdict(list) for a, ss in E.items(): for s in ss: rev[s].append(a) ends = {o for o, bid, _ in isl.call_sites(b) if bid in (6, 62)} R, q = set(ends), collections.deque(ends) while q: n = q.popleft() for p in rev.get(n, ()): if p not in R: R.add(p); q.append(p) out = [] s1, s2 = isl.symbols(b, 1), isl.symbols(b, 2) for c in isl_cfg.conditions(b, s1, s2): br = nxt.get(c['off']) if br is None: continue taken, fallth = c['target'], nxt.get(br) t, f = taken in R, (fallth in R if fallth else False) if t != f: out.append({**c, 'ends_when': 'taken' if t else 'not-taken'}) return out, len(R), len(offs), len(ends)