|
|
|
@@ -58,8 +58,8 @@ def realize_assign(ctx:dict[UOp, None], a:UOp) -> None:
|
|
|
|
|
do_realize = PatternMatcher([
|
|
|
|
|
# always realize SINK parents
|
|
|
|
|
(UPat(Ops.SINK, name="s"), lambda ctx,s: ctx.update((x.base, None) for x in s.src if x.base.op not in ALWAYS_CONTIGUOUS)),
|
|
|
|
|
# always realize ASSIGN/COPY/BUFFER_VIEW
|
|
|
|
|
(UPat({Ops.ASSIGN, Ops.COPY, Ops.BUFFER_VIEW}, name="tr"), realize),
|
|
|
|
|
# always realize ASSIGN/COPY/BUFFER_VIEW/CONTIGUOUS
|
|
|
|
|
(UPat({Ops.ASSIGN, Ops.COPY, Ops.BUFFER_VIEW, Ops.CONTIGUOUS}, name="tr"), realize),
|
|
|
|
|
# realize parents of COPY, MSELECT, MSTACK
|
|
|
|
|
(UPat((Ops.COPY, Ops.MSELECT, Ops.MSTACK), name="rb"), realize_parents),
|
|
|
|
|
# realize input to assign (might be optimized out)
|
|
|
|
@@ -67,7 +67,7 @@ do_realize = PatternMatcher([
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
add_contiguous = PatternMatcher([
|
|
|
|
|
(UPat(GroupOp.All-{Ops.CONTIGUOUS}, name="x"), lambda ctx,x: x.replace(tag=1).contiguous() if x in ctx and x.tag is None else None),
|
|
|
|
|
(UPat(GroupOp.All, name="x"), lambda ctx,x: x.replace(tag=1).realize() if x in ctx and x.tag is None else None),
|
|
|
|
|
])
|
|
|
|
|
remove_tags = PatternMatcher([(UPat(GroupOp.All, name="x"), lambda x: x.replace(tag=None) if x.tag is not None else None)])
|
|
|
|
|
|
|
|
|
@@ -174,7 +174,15 @@ pm_mops = PatternMatcher([
|
|
|
|
|
(UPat(Ops.PAD, name="r").f(Ops.INDEX, allow_any_len=True, name="idx"), map_pad),
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
def map_partial_contiguous(ctx:RangeifyContext, x:UOp, idx:UOp):
|
|
|
|
|
# 3b. rangeify (ops)
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
class BufferizeOpts:
|
|
|
|
|
# on AddrSpace.LOCAL, device is the id
|
|
|
|
|
device: str|tuple[str, ...]|int
|
|
|
|
|
addrspace: AddrSpace = AddrSpace.GLOBAL
|
|
|
|
|
|
|
|
|
|
def map_partial_realize(ctx:RangeifyContext, x:UOp, idx:UOp):
|
|
|
|
|
if x.arg is None: return None # map_contiguous can handle this
|
|
|
|
|
# NOTE: all partial contiguous can safely be replaced by full contiguous. we should be able to match old functionality like this
|
|
|
|
|
if not (RANGEIFY > 1): return idx.replace(src=(x.replace(arg=None),)+idx.src[1:])
|
|
|
|
@@ -188,15 +196,15 @@ def map_partial_contiguous(ctx:RangeifyContext, x:UOp, idx:UOp):
|
|
|
|
|
passthrough_idx.append(idx.src[1+i])
|
|
|
|
|
ranges.append(ctx.new_range(s) if resolve(s!=1) else UOp.const(dtypes.index, 0))
|
|
|
|
|
new_ranges.append(ranges[-1])
|
|
|
|
|
ret = x.src[0].index(*ranges).bufferize(*[x for x in new_ranges if x.op is not Ops.CONST], arg=x.device)
|
|
|
|
|
ret = x.src[0].index(*ranges).bufferize(*[x for x in new_ranges if x.op is not Ops.CONST], arg=BufferizeOpts(device=x.device))
|
|
|
|
|
return ret.index(*passthrough_idx)
|
|
|
|
|
|
|
|
|
|
def map_contiguous(ctx:RangeifyContext, x:UOp):
|
|
|
|
|
def map_realize(ctx:RangeifyContext, x:UOp):
|
|
|
|
|
if x.arg is not None: return None
|
|
|
|
|
ranges = []
|
|
|
|
|
for s in x.shape[len(x.src)-1:]:
|
|
|
|
|
ranges.append(ctx.new_range(s) if resolve(s!=1) else UOp.const(dtypes.index, 0))
|
|
|
|
|
ret = x.src[0].index(*ranges).bufferize(*x.src[1:], *[x for x in ranges if x.op is not Ops.CONST], arg=x.device)
|
|
|
|
|
ret = x.src[0].index(*ranges).bufferize(*x.src[1:], *[x for x in ranges if x.op is not Ops.CONST], arg=BufferizeOpts(device=x.device))
|
|
|
|
|
# was there a shrink? move this before the bufferize?
|
|
|
|
|
# TODO: do we need this?
|
|
|
|
|
if resolve(prod(x.shape) != prod(ret.shape)): ret = ret.forced_reshape((prod(ret.shape),)).shrink(((0, prod(x.shape)),))
|
|
|
|
@@ -242,7 +250,7 @@ def index_child(ctx:RangeifyContext, c:UOp, x:UOp, idx:UOp):
|
|
|
|
|
# index based on the shared ranges
|
|
|
|
|
ret = c.index(*out_rngs)
|
|
|
|
|
# if all ranges aren't the same between children, we have to bufferize
|
|
|
|
|
if len(idx_ranges) > 0: ret = ret.bufferize(*end_ranges, arg=x.device).index(*[idx.src[1+i] for i in idx_ranges])
|
|
|
|
|
if len(idx_ranges) > 0: ret = ret.bufferize(*end_ranges, arg=BufferizeOpts(device=x.device)).index(*[idx.src[1+i] for i in idx_ranges])
|
|
|
|
|
return ret
|
|
|
|
|
|
|
|
|
|
def children_gate(ctx:RangeifyContext, idx:UOp, c:UOp):
|
|
|
|
@@ -258,14 +266,14 @@ def might_end_axis(idx:UOp):
|
|
|
|
|
for i,a in enumerate(idx.src[1:]):
|
|
|
|
|
if any(x.arg > idx.arg for x in a.toposort() if x.op is Ops.RANGE):
|
|
|
|
|
to_end_axis.append(i)
|
|
|
|
|
if to_end_axis: return idx.replace(src=(idx.src[0].contiguous(arg=tuple(to_end_axis)),)+idx.src[1:], arg=None)
|
|
|
|
|
if to_end_axis: return idx.replace(src=(idx.src[0].realize(arg=tuple(to_end_axis)),)+idx.src[1:], arg=None)
|
|
|
|
|
return idx.replace(arg=None)
|
|
|
|
|
|
|
|
|
|
pm_rangeify = pm_mops+PatternMatcher([
|
|
|
|
|
# sink contigs to kick it off
|
|
|
|
|
(UPat(Ops.CONTIGUOUS, src=(UPat(),), name="x", allow_any_len=True), map_contiguous),
|
|
|
|
|
(UPat(Ops.REALIZE, src=(UPat(),), name="x", allow_any_len=True), map_realize),
|
|
|
|
|
# if there's an INDEX it can support partial contig
|
|
|
|
|
(UPat(Ops.INDEX, src=(UPat(Ops.CONTIGUOUS, src=(UPat(),), name="x"),), allow_any_len=True, name="idx"), map_partial_contiguous),
|
|
|
|
|
(UPat(Ops.INDEX, src=(UPat(Ops.REALIZE, src=(UPat(),), name="x"),), allow_any_len=True, name="idx"), map_partial_realize),
|
|
|
|
|
|
|
|
|
|
# if there are new ended children, tag the SINK
|
|
|
|
|
(UPat(Ops.INDEX, src=(UPat(Ops.CHILD, src=(UPat(name="c"), ), name="x"),), allow_any_len=True, name="idx"), index_child),
|
|
|
|
@@ -281,7 +289,8 @@ pm_rangeify = pm_mops+PatternMatcher([
|
|
|
|
|
(UPat(Ops.INDEX, src=(UPat(GroupOp.Elementwise.union({Ops.REDUCE_AXIS})),), allow_any_len=True, name="idx"), might_end_axis),
|
|
|
|
|
|
|
|
|
|
# move MAP through elementwise ALU / reduce. these are the items with cost
|
|
|
|
|
(UPat(Ops.INDEX, src=(UPat(GroupOp.Elementwise.union({Ops.STORE, Ops.ASSIGN, Ops.COPY, Ops.DEVICE, Ops.BIND})),), allow_any_len=True, name="x"),
|
|
|
|
|
(UPat(Ops.INDEX, src=(UPat(GroupOp.Elementwise.union(
|
|
|
|
|
{Ops.STORE, Ops.ASSIGN, Ops.COPY, Ops.DEVICE, Ops.BIND, Ops.CONTIGUOUS})),), allow_any_len=True, name="x"),
|
|
|
|
|
lambda x: x.src[0].replace(src=tuple([s.index(*x.src[1:]) for s in x.src[0].src]))),
|
|
|
|
|
(UPat(Ops.INDEX, src=(UPat(Ops.REDUCE_AXIS, name="red"),), allow_any_len=True, name="idx"), map_reduce),
|
|
|
|
|
])
|
|
|
|
@@ -349,17 +358,17 @@ def bufferize_to_store(x:UOp, locals_allowed=False):
|
|
|
|
|
shape = tuple([int(r.vmax+1) for r in rngs])
|
|
|
|
|
size = prod(shape)
|
|
|
|
|
assert size > 0, f"no zero sized buffers {shape}"
|
|
|
|
|
sdtype = x.dtype.ptr(size=size, addrspace=AddrSpace.GLOBAL if not isinstance(x.arg, tuple) else x.arg[0])
|
|
|
|
|
sdtype = x.dtype.ptr(size=size, addrspace=x.arg.addrspace)
|
|
|
|
|
if x.src[0].op is Ops.ASSIGN:
|
|
|
|
|
assign_target, assign_src = x.src[0].src
|
|
|
|
|
assert assign_target.op is Ops.INDEX
|
|
|
|
|
return assign_target.replace(dtype=sdtype).store(assign_src, *rngs, dtype=sdtype).forced_reshape(shape, dtype=x.dtype)
|
|
|
|
|
# NOTE: the DEFINE_LOCAL needs to be disambiguated here
|
|
|
|
|
if sdtype.addrspace == AddrSpace.GLOBAL:
|
|
|
|
|
buf = UOp.new_buffer(x.arg, size, x.dtype)
|
|
|
|
|
buf = UOp.new_buffer(x.arg.device, size, x.dtype)
|
|
|
|
|
else:
|
|
|
|
|
if not locals_allowed: return None
|
|
|
|
|
buf = UOp(Ops.DEFINE_LOCAL, sdtype, arg=x.arg[1])
|
|
|
|
|
buf = UOp(Ops.DEFINE_LOCAL, sdtype, arg=x.arg.device)
|
|
|
|
|
return buf.reshape(shape).index(*rngs, dtype=sdtype).store(x.src[0], *rngs, dtype=sdtype).forced_reshape(shape, dtype=x.dtype)
|
|
|
|
|
|
|
|
|
|
pm_add_buffers_local = pm_mops+PatternMatcher([
|
|
|
|
@@ -411,6 +420,10 @@ to_define_global = PatternMatcher([
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
rangeify_codegen = PatternMatcher([
|
|
|
|
|
# no CONTIGUOUS in the kernel graph
|
|
|
|
|
# TODO: this can be moved into codegen?
|
|
|
|
|
(UPat(Ops.CONTIGUOUS, name="x"), lambda x: x.src[0]),
|
|
|
|
|
|
|
|
|
|
# add loads to non ptr indexes
|
|
|
|
|
# TODO: this can be moved into codegen?
|
|
|
|
|
(UPat((Ops.DEFINE_GLOBAL, Ops.STORE), name="dg").f(Ops.INDEX, name="idx", allow_any_len=True),
|
|
|
|
@@ -444,7 +457,7 @@ def get_rangeify_map(sink:UOp) -> dict[UOp, UOp]:
|
|
|
|
|
tensor_map = graph_rewrite_map(sink, multi_pm+earliest_rewrites, name="earliest")
|
|
|
|
|
realize_map: dict[UOp, UOp] = {}
|
|
|
|
|
graph_rewrite(tensor_map[sink], do_realize, ctx=realize_map, name="Input Graph")
|
|
|
|
|
tensor_map = graph_rewrite_map(tensor_map[sink], add_contiguous, ctx=realize_map, bottom_up=True, input_map=tensor_map, name="add contiguous")
|
|
|
|
|
tensor_map = graph_rewrite_map(tensor_map[sink], add_contiguous, ctx=realize_map, bottom_up=True, input_map=tensor_map, name="add realize")
|
|
|
|
|
tensor_map = graph_rewrite_map(tensor_map[sink], remove_tags, input_map=tensor_map, name="remove tags")
|
|
|
|
|
tensor_map = graph_rewrite_map(tensor_map[sink], pm_children, ctx=ChildrenContext(), bottom_up=True, input_map=tensor_map, name="children")
|
|
|
|
|
tensor_map = graph_rewrite_map(tensor_map[sink], pm_rangeify, ctx=RangeifyContext(), bottom_up=True, input_map=tensor_map, name="rangeify")
|
|
|
|
|