mirror of
https://github.com/tinygrad/tinygrad.git
synced 2026-08-29 18:36:07 +00:00
replace all sparents with toposort (#7983)
This commit is contained in:
@@ -96,7 +96,7 @@ class TestLinearizer(unittest.TestCase):
|
||||
lin = helper_linearizer_ast(sink, [a_t, b_t], wanna_output=[a_t.numpy()+b_t.numpy(), a_t.numpy()*b_t.numpy()])[0]
|
||||
|
||||
stores = [u for u in lin.uops if u.op is Ops.STORE]
|
||||
mutable_bufs = dedup(flatten([[x for x in u.src[0].sparents if x.op is Ops.DEFINE_GLOBAL] for u in stores]))
|
||||
mutable_bufs = dedup(flatten([[x for x in u.src[0].toposort if x.op is Ops.DEFINE_GLOBAL] for u in stores]))
|
||||
assert len(mutable_bufs) == len(stores) == 2
|
||||
assert [u.arg for u in mutable_bufs] == [0, 1]
|
||||
|
||||
@@ -988,10 +988,10 @@ class TestLinearizer(unittest.TestCase):
|
||||
|
||||
# the first store is to lds and can be upcasted
|
||||
assert stores[0].src[-1].dtype == dtypes.float.vec(4)
|
||||
assert any(x.op is Ops.DEFINE_LOCAL for x in stores[0].sparents)
|
||||
assert any(x.op is Ops.DEFINE_LOCAL for x in stores[0].toposort)
|
||||
# the second store is to gds with no upcasts
|
||||
assert stores[1].src[-1].dtype == dtypes.float
|
||||
assert any(x.op is Ops.DEFINE_GLOBAL for x in stores[1].sparents)
|
||||
assert any(x.op is Ops.DEFINE_GLOBAL for x in stores[1].toposort)
|
||||
|
||||
def test_zero_fold(self):
|
||||
a, b = Tensor.randn(1).realize(), Tensor.randn(1).realize()
|
||||
@@ -1155,7 +1155,7 @@ class TestLinearizer(unittest.TestCase):
|
||||
def test_grouped_dims(self):
|
||||
def _assert_grouped_dims(prefix, dims, max_sizes, reverse_dims, expected_sizes):
|
||||
idxs = get_grouped_dims(prefix, dims, max_sizes, reverse_dims)
|
||||
loop_idxs = dedup(flatten([[y for y in x.sparents if y.op is Ops.SPECIAL] for x in idxs]))
|
||||
loop_idxs = dedup(flatten([[y for y in x.toposort if y.op is Ops.SPECIAL] for x in idxs]))
|
||||
loop_idxs = sorted(loop_idxs, key=lambda uop: uop.arg[0])
|
||||
sizes = [x.arg[1] for x in loop_idxs]
|
||||
assert len(idxs) == len(dims), f"expected idxs to have same length as dims {len(dims)}, got {len(idxs)}"
|
||||
|
||||
@@ -95,7 +95,7 @@ class TestLinearizerDumb(unittest.TestCase):
|
||||
print(prg.src)
|
||||
if_uops = [u for u in k.uops if u.op is Ops.IF]
|
||||
self.assertIn(len(if_uops), {1,2,3})
|
||||
conditions = if_uops[0].src[0].sparents
|
||||
conditions = if_uops[0].src[0].toposort
|
||||
self.assertLessEqual(len(conditions), 9)
|
||||
|
||||
# this was a bug in embedding, someday we should fold this anyway
|
||||
|
||||
@@ -1045,7 +1045,7 @@ class TestLinearizerFailures(unittest.TestCase):
|
||||
ifs = [u for u in k.uops if u.op is Ops.IF]
|
||||
self.assertEqual(len(ifs), 3)
|
||||
#for st in k.uops.sink.src: self.assertEqual(len(st.src), 4)
|
||||
self.assertLessEqual(len(ifs[0].src[0].sparents), 17)
|
||||
self.assertLessEqual(len(ifs[0].src[0].toposort), 17)
|
||||
|
||||
def test_failure_45(self):
|
||||
ast = UOp(Ops.SINK, dtypes.void, arg=None, src=(
|
||||
|
||||
@@ -1673,7 +1673,7 @@ class TestIndexing(unittest.TestCase):
|
||||
@track_rewrites(named=True)
|
||||
def swizzle_rewrite(u:UOp) -> UOp: return graph_rewrite(graph_rewrite(u, view_left), view_right)
|
||||
|
||||
def swizzle_cnt(u:UOp) -> int: return len([x for x in u.sparents if x.op is Ops.VIEW and len(x.src) != 0])
|
||||
def swizzle_cnt(u:UOp) -> int: return len([x for x in u.toposort if x.op is Ops.VIEW and len(x.src) != 0])
|
||||
|
||||
class TestSwizzle(unittest.TestCase):
|
||||
def test_swizzle_simple(self):
|
||||
|
||||
+12
-12
@@ -60,7 +60,7 @@ class TestGraphRewriteEfficiency(unittest.TestCase):
|
||||
new_sink = full_graph_rewrite(lower_sink)
|
||||
et = time.perf_counter() - st
|
||||
UOp.__init__ = old_init
|
||||
print(f"rewrote in {et*1000:.2f} ms, from {len(lower_sink.sparents)} -> {len(new_sink.sparents)}, creating {cnt[0]} uops")
|
||||
print(f"rewrote in {et*1000:.2f} ms, from {len(lower_sink.toposort)} -> {len(new_sink.toposort)}, creating {cnt[0]} uops")
|
||||
|
||||
class TestGraphRewriteConst(unittest.TestCase):
|
||||
def test_gep_const(self):
|
||||
@@ -106,7 +106,7 @@ class TestGraphRewrite(unittest.TestCase):
|
||||
a1 = UOp(Ops.DEFINE_VAR, dtypes.int, (), ("a1", UOp.const(dtypes.int, 0), UOp.const(dtypes.int, 11)))
|
||||
a2 = UOp(Ops.DEFINE_VAR, dtypes.int, (), ("a2", UOp.const(dtypes.int, 0), UOp.const(dtypes.int, 11)))
|
||||
sink = a1.sink(a2)
|
||||
define_vars = [x for x in graph_rewrite(sink, PatternMatcher([])).sparents if x.op is Ops.DEFINE_VAR]
|
||||
define_vars = [x for x in graph_rewrite(sink, PatternMatcher([])).toposort if x.op is Ops.DEFINE_VAR]
|
||||
self.assertEqual(len(define_vars), 1)
|
||||
|
||||
def test_simple(self):
|
||||
@@ -187,7 +187,7 @@ class TestGraphRewrite(unittest.TestCase):
|
||||
print(sink.render())
|
||||
self.assertEqual(sink.op, Ops.ADD)
|
||||
self.assertEqual(sink.src[1].op, Ops.CONST)
|
||||
self.assertEqual(len([x for x in sink.sparents if x.op is Ops.CONST]), 1)
|
||||
self.assertEqual(len([x for x in sink.toposort if x.op is Ops.CONST]), 1)
|
||||
|
||||
class TestUOpGraph(unittest.TestCase):
|
||||
def test_add_constant_fold(self):
|
||||
@@ -600,14 +600,14 @@ class TestLoadStoreFolder(unittest.TestCase):
|
||||
sink = UOp(Ops.VECTORIZE, dtypes.float.vec(len(load)), tuple(load))
|
||||
|
||||
sink = float4_rewrite(sink.sink())
|
||||
assert len([x for x in sink.sparents if x.op is Ops.LOAD]) == 1
|
||||
assert len([x for x in sink.toposort if x.op is Ops.LOAD]) == 1
|
||||
|
||||
def test_two_load_fold(self):
|
||||
buf = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr())
|
||||
load = [UOp(Ops.LOAD, dtypes.float, (buf.index(UOp.const(dtypes.int, i)),)) for i in range(8)]
|
||||
sink = UOp(Ops.VECTORIZE, dtypes.float.vec(len(load)), tuple(load))
|
||||
sink = float4_rewrite(sink.sink())
|
||||
assert len([x for x in sink.sparents if x.op is Ops.LOAD]) == 2
|
||||
assert len([x for x in sink.toposort if x.op is Ops.LOAD]) == 2
|
||||
|
||||
def test_simple_load_fold_gated(self):
|
||||
buf = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr())
|
||||
@@ -615,8 +615,8 @@ class TestLoadStoreFolder(unittest.TestCase):
|
||||
load = [UOp(Ops.LOAD, dtypes.float, (buf.index(UOp.const(dtypes.int, i), gate),)) for i in range(4)]
|
||||
sink = UOp(Ops.VECTORIZE, dtypes.float.vec(len(load)), tuple(load))
|
||||
sink = float4_rewrite(sink.sink())
|
||||
assert len([x for x in sink.sparents if x.op is Ops.LOAD]) == 1
|
||||
single_load = [x for x in sink.sparents if x.op is Ops.LOAD][0]
|
||||
assert len([x for x in sink.toposort if x.op is Ops.LOAD]) == 1
|
||||
single_load = [x for x in sink.toposort if x.op is Ops.LOAD][0]
|
||||
self.assertEqual(single_load.src[1].op, Ops.VECTORIZE)
|
||||
|
||||
def test_simple_load_dont_fold_different_gated(self):
|
||||
@@ -627,14 +627,14 @@ class TestLoadStoreFolder(unittest.TestCase):
|
||||
UOp.const(dtypes.float, 0))) for i in range(4)]
|
||||
sink = UOp(Ops.VECTORIZE, dtypes.float.vec(len(load)), tuple(load))
|
||||
sink = float4_rewrite(sink.sink())
|
||||
assert len([x for x in sink.sparents if x.op is Ops.LOAD]) == 3
|
||||
assert len([x for x in sink.toposort if x.op is Ops.LOAD]) == 3
|
||||
|
||||
def test_simple_store_fold(self):
|
||||
buf = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr())
|
||||
load = [UOp(Ops.STORE, dtypes.float, (buf.index(UOp.const(dtypes.int, i)), UOp.const(dtypes.float, 0))) for i in range(4)]
|
||||
sink = UOp(Ops.SINK, dtypes.void, tuple(load))
|
||||
sink = float4_rewrite(sink)
|
||||
assert len([x for x in sink.sparents if x.op is Ops.STORE]) == 1
|
||||
assert len([x for x in sink.toposort if x.op is Ops.STORE]) == 1
|
||||
|
||||
def test_simple_store_fold_gate(self):
|
||||
buf = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr())
|
||||
@@ -642,8 +642,8 @@ class TestLoadStoreFolder(unittest.TestCase):
|
||||
load = [UOp(Ops.STORE, dtypes.float, (buf.index(UOp.const(dtypes.int, i)), UOp.const(dtypes.float, 0), gate)) for i in range(4)]
|
||||
sink = UOp(Ops.SINK, dtypes.void, tuple(load))
|
||||
sink = float4_rewrite(sink)
|
||||
assert len([x for x in sink.sparents if x.op is Ops.STORE]) == 1
|
||||
one_store = [x for x in sink.sparents if x.op is Ops.STORE][0]
|
||||
assert len([x for x in sink.toposort if x.op is Ops.STORE]) == 1
|
||||
one_store = [x for x in sink.toposort if x.op is Ops.STORE][0]
|
||||
assert len(one_store.src) == 3
|
||||
_if_node = one_store.src[2]
|
||||
assert _if_node.op == Ops.IF and _if_node.src[0] == gate
|
||||
@@ -656,7 +656,7 @@ class TestLoadStoreFolder(unittest.TestCase):
|
||||
UOp.const(dtypes.float, i))) for i in range(4)]
|
||||
sink = UOp(Ops.SINK, dtypes.void, tuple(load))
|
||||
sink = float4_rewrite(sink)
|
||||
assert len([x for x in sink.sparents if x.op is Ops.STORE]) == 3
|
||||
assert len([x for x in sink.toposort if x.op is Ops.STORE]) == 3
|
||||
|
||||
class TestIFUOps(unittest.TestCase):
|
||||
def test_create_ifs(self):
|
||||
|
||||
@@ -130,8 +130,8 @@ def linearize_uop(sink:UOp, skip_check:bool=not __debug__) -> List[UOp]:
|
||||
sink = graph_rewrite(sink, make_basic_blocks, ctx=(block_ctxs, children))
|
||||
|
||||
# add BLOCKFORK (slow!)
|
||||
block_parent_count = collections.Counter(flatten([x.src for x in sink.sparents if x.op is Ops.BLOCK]))
|
||||
non_block_parents = flatten([x.src for x in sink.sparents if x.op is not Ops.BLOCK])
|
||||
block_parent_count = collections.Counter(flatten([x.src for x in sink.toposort if x.op is Ops.BLOCK]))
|
||||
non_block_parents = flatten([x.src for x in sink.toposort if x.op is not Ops.BLOCK])
|
||||
forks = {}
|
||||
for u,child_count in block_parent_count.items():
|
||||
if u.op not in DONT_PLACE_IN_BLOCK and child_count > 1 and u not in non_block_parents:
|
||||
@@ -142,7 +142,7 @@ def linearize_uop(sink:UOp, skip_check:bool=not __debug__) -> List[UOp]:
|
||||
|
||||
# combine matching BLOCKENDS
|
||||
blockends_to_arg: Dict[UOp, List[UOp]] = {}
|
||||
for be in sink.sparents:
|
||||
for be in sink.toposort:
|
||||
if be.op is Ops.BLOCKEND: blockends_to_arg.setdefault(be.arg.end, []).append(be)
|
||||
new_forks = {}
|
||||
for k,v in blockends_to_arg.items():
|
||||
|
||||
@@ -55,7 +55,7 @@ def get_index(ast:UOp, opts:Renderer) -> IndexContext:
|
||||
full_shape = ast.full_shape
|
||||
first_upcasted = len(full_shape)-ki.upcasted
|
||||
# if there's no reduce, this is first_upcasted. assumes reduces are at the end
|
||||
first_reduce = min([first_upcasted]+flatten(x.axis_arg for x in ast.sparents if x.op is Ops.REDUCE_AXIS))
|
||||
first_reduce = min([first_upcasted]+flatten(x.axis_arg for x in ast.toposort if x.op is Ops.REDUCE_AXIS))
|
||||
local_loads = [x for x in ast.parents if x.op is Ops.LOAD and x.src[0].op is Ops.DEFINE_LOCAL]
|
||||
# NOTE: sum up the reduced axes looking across all local loads, yields the number of grouped reduces
|
||||
group_for_reduces = sum([any(l.st_arg.shape[i]!=ast.src[0].st_arg.shape[i] for l in local_loads) for i in range(first_reduce,first_upcasted)])
|
||||
|
||||
@@ -221,7 +221,7 @@ def no_vectorized_wmma(wmma:UOp):
|
||||
return UOp(Ops.VECTORIZE, wmma.dtype, tuple(wmma_ex))
|
||||
|
||||
def reduce_collapse(acc:UOp, ret:UOp, alu:UOp):
|
||||
reduce_parented, reduce_unparented = partition(acc.src[1:], lambda x: x in ret.sparents)
|
||||
reduce_parented, reduce_unparented = partition(acc.src[1:], lambda x: x in ret.toposort)
|
||||
if len(reduce_unparented) == 0: return None
|
||||
new_acc = acc.replace(src=acc.src[0:1]+tuple(reduce_parented))
|
||||
ret = new_acc.assign(new_acc.alu(alu.op, ret))
|
||||
@@ -447,7 +447,7 @@ devectorize = PatternMatcher([
|
||||
])
|
||||
|
||||
def delete_redundant_gates(buf:UOp, idx:UOp, val:UOp, store_gate:UOp, cast:Optional[UOp]=None) -> Optional[UOp]:
|
||||
if store_gate not in [gate.src[0] for gate in val.sparents if gate.op is Ops.IF]: return None
|
||||
if store_gate not in [gate.src[0] for gate in val.toposort if gate.op is Ops.IF]: return None
|
||||
# remove the gate from the index
|
||||
return UOp.store(buf.index(idx).cast(cast.dtype) if cast is not None else buf.index(idx), val)
|
||||
|
||||
|
||||
+8
-11
@@ -250,10 +250,7 @@ class UOp(MathTrait, metaclass=UOpMetaClass):
|
||||
def argstr(self): return f'({", ".join(map(str, self.arg))})' if self.op is Ops.REDUCE_AXIS else self.arg
|
||||
@functools.cached_property
|
||||
def parents(self) -> Dict[UOp, None]: return {**{x:None for x in self.src}, **{k:None for x in self.src for k in x.parents}}
|
||||
@functools.cached_property # parents with self
|
||||
def sparents(self) -> Dict[UOp, None]: return {**self.parents, self:None}
|
||||
|
||||
# TODO: replace usage of sparents with this
|
||||
@functools.cached_property
|
||||
def toposort(self) -> Dict[UOp, None]:
|
||||
nodes: Dict[UOp, None] = {}
|
||||
@@ -422,12 +419,12 @@ class UOp(MathTrait, metaclass=UOpMetaClass):
|
||||
@property
|
||||
def val(self) -> int: return self.unbind()[1]
|
||||
def vars(self) -> Set[UOp]:
|
||||
bound_vars = set([x for x in self.sparents if x.op is Ops.BIND and x.src[0].op is Ops.DEFINE_VAR])
|
||||
bound_vars = set([x for x in self.toposort if x.op is Ops.BIND and x.src[0].op is Ops.DEFINE_VAR])
|
||||
bound_var_base = set(x.src[0] for x in bound_vars)
|
||||
all_vars = set([x for x in self.sparents if x.op is Ops.DEFINE_VAR])
|
||||
all_vars = set([x for x in self.toposort if x.op is Ops.DEFINE_VAR])
|
||||
return bound_vars.union(set([x for x in all_vars if x not in bound_var_base]))
|
||||
def variables(self) -> List[Variable]:
|
||||
st_vars: List[Set[Variable]] = [x.st_arg.vars() for x in self.sparents if x.op in GroupOp.Buffer]
|
||||
st_vars: List[Set[Variable]] = [x.st_arg.vars() for x in self.toposort if x.op in GroupOp.Buffer]
|
||||
return sorted(set.union(*st_vars, [x.unbind()[0] if x.op is not Ops.DEFINE_VAR else x for x in self.vars()]), key=lambda v: v.arg)
|
||||
|
||||
# *** uop symbolic stuff ***
|
||||
@@ -484,7 +481,7 @@ class UOp(MathTrait, metaclass=UOpMetaClass):
|
||||
@functools.cached_property
|
||||
def _sym_fxn(self):
|
||||
sself = self.simplify()
|
||||
varnames = tuple(x.arg[0] for x in sself.sparents if x.op is Ops.DEFINE_VAR)
|
||||
varnames = tuple(x.arg[0] for x in sself.toposort if x.op is Ops.DEFINE_VAR)
|
||||
# TODO: sanitize varnames, or don't use naked eval while staying fast
|
||||
return eval("lambda "+','.join(varnames)+": "+sself.render()), varnames # pylint: disable=eval-used
|
||||
|
||||
@@ -542,10 +539,10 @@ def flops_mem(uops:List[UOp], ignore_indexing=False) -> Tuple[sint, sint]:
|
||||
if ignore_indexing:
|
||||
for u in uops:
|
||||
if u.op in {Ops.LOAD, Ops.STORE}:
|
||||
dont_count = dont_count.union(u.src[0].sparents)
|
||||
if len(u.src) > 2: dont_count = dont_count.union(u.src[2].sparents)
|
||||
dont_count = dont_count.union(u.src[0].toposort)
|
||||
if len(u.src) > 2: dont_count = dont_count.union(u.src[2].toposort)
|
||||
elif u.op is Ops.IF:
|
||||
dont_count = dont_count.union(u.src[0].sparents)
|
||||
dont_count = dont_count.union(u.src[0].toposort)
|
||||
for u in uops:
|
||||
if u.op is Ops.RANGE:
|
||||
mult_stack.append(mults)
|
||||
@@ -1056,7 +1053,7 @@ def uop_given_valid(valid:UOp, uop:UOp) -> Optional[UOp]:
|
||||
# if the constraint is a simplex: X0 + X1 + ... > 0, we can check if all Xi > 0 simplify into the same output
|
||||
candidates.append([(Xi, UOp.variable("fake", 1, Xi.vmax, Xi.dtype)) for Xi in split_uop(expr, Ops.ADD)])
|
||||
# try checking the whole clause
|
||||
if expr in uop.sparents:
|
||||
if expr in uop.toposort:
|
||||
candidates.append([(expr, UOp.variable("fake", expr.vmin if v[0] is None else v[0], expr.vmax if v[1] is None else v[1], expr.dtype))])
|
||||
|
||||
for candidate in candidates:
|
||||
|
||||
@@ -44,7 +44,7 @@ class ProgramSpec:
|
||||
for u in self.uops:
|
||||
if u.op is Ops.DEFINE_VAR: self.vars.append(u)
|
||||
if u.op is Ops.DEFINE_GLOBAL: self.globals.append(u.arg)
|
||||
if u.op is Ops.STORE: self.outs.extend([x.arg for x in u.src[0].sparents if x.op is Ops.DEFINE_GLOBAL])
|
||||
if u.op is Ops.STORE: self.outs.extend([x.arg for x in u.src[0].toposort if x.op is Ops.DEFINE_GLOBAL])
|
||||
if u.op is Ops.SPECIAL:
|
||||
# NOTE: you have to set local_size and global_size to the base [1,1,1] outside this
|
||||
if u.arg[0][0] == 'i': self.local_size = None
|
||||
|
||||
@@ -127,7 +127,7 @@ class CStyleLanguage(Renderer):
|
||||
|
||||
# mark buffers that we store to writable
|
||||
if u.op is Ops.STORE:
|
||||
for up in u.src[0].sparents:
|
||||
for up in u.src[0].toposort:
|
||||
if up.op is Ops.DEFINE_GLOBAL: bufs[up] = (bufs[up][0], (bufs[up][1][0], True))
|
||||
|
||||
# naming
|
||||
|
||||
@@ -81,17 +81,17 @@ class ShapeTracker:
|
||||
if c.op is Ops.RANGE: ret[c.arg[0]] = 1
|
||||
if c.op is Ops.MUL and c.src[0].op is Ops.RANGE and c.src[1].op is Ops.CONST: ret[c.src[0].arg[0]] = c.src[1].arg
|
||||
if c.op is Ops.MUL and c.src[1].op is Ops.RANGE and c.src[0].op is Ops.CONST: ret[c.src[1].arg[0]] = c.src[0].arg
|
||||
used_ranges = [x.arg[0] for x in idx.sparents if x.op is Ops.RANGE]
|
||||
used_ranges = [x.arg[0] for x in idx.toposort if x.op is Ops.RANGE]
|
||||
ret = [x if i in used_ranges else 0 for i,x in enumerate(ret)]
|
||||
if not ignore_valid:
|
||||
for masked_axis in [x.arg[0] for x in valid.sparents if x.op is Ops.RANGE]: ret[masked_axis] = None
|
||||
for masked_axis in [x.arg[0] for x in valid.toposort if x.op is Ops.RANGE]: ret[masked_axis] = None
|
||||
return tuple(ret)
|
||||
|
||||
def unit_stride_axes(self, ignore_valid=False) -> List[int]: return [i for i,st in enumerate(self.real_strides(ignore_valid)) if st == 1]
|
||||
|
||||
def axis_is_masked(self, axis:int) -> bool:
|
||||
_, valid = self.to_indexed_uops()
|
||||
return axis in [x.arg[0] for x in graph_rewrite(valid, symbolic_flat).sparents if x.op is Ops.RANGE]
|
||||
return axis in [x.arg[0] for x in graph_rewrite(valid, symbolic_flat).toposort if x.op is Ops.RANGE]
|
||||
|
||||
def simplify(self) -> ShapeTracker:
|
||||
if len(self.views) >= 2 and (new_view := self.views[-2] + self.views[-1]) is not None:
|
||||
|
||||
@@ -60,7 +60,7 @@ def get_metadata(contexts:List[Tuple[Any, List[TrackedRewriteContext]]]) -> List
|
||||
def uop_to_json(x:UOp) -> Dict[int, Tuple[str, str, List[int], str, str]]:
|
||||
assert isinstance(x, UOp)
|
||||
graph: Dict[int, Tuple[str, str, List[int], str, str]] = {}
|
||||
for u in x.sparents:
|
||||
for u in x.toposort:
|
||||
if u.op is Ops.CONST: continue
|
||||
label = f"{str(u.op).split('.')[1]}{(' '+word_wrap(str(u.arg).replace(':', ''))) if u.arg is not None else ''}\n{str(u.dtype)}"
|
||||
for idx,x in enumerate(u.src):
|
||||
@@ -92,7 +92,7 @@ def get_details(k:Any, ctx:TrackedRewriteContext, metadata:GraphRewriteMetadata)
|
||||
# sanity check
|
||||
if new_sink is sink: raise AssertionError(f"rewritten sink wasn't rewritten! {i} {unwrap(upat).location}")
|
||||
# update ret data
|
||||
g.changed_nodes.append([id(x) for x in u1.sparents if x.op is not Ops.CONST])
|
||||
g.changed_nodes.append([id(x) for x in u1.toposort if x.op is not Ops.CONST])
|
||||
g.diffs.append(list(difflib.unified_diff(pcall(str, u0).splitlines(), pcall(str, u1).splitlines())))
|
||||
g.graphs.append(sink:=new_sink)
|
||||
return g
|
||||
|
||||
Reference in New Issue
Block a user