mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-28 09:03:46 +08:00
132 lines
7.0 KiB
Python
132 lines
7.0 KiB
Python
import unittest
|
|
from tinygrad.codegen.opt import Opt, OptOps
|
|
from tinygrad.uop.ops import Ops, GroupOp, AxisType
|
|
from tinygrad.device import Device
|
|
from tinygrad.tensor import Tensor
|
|
from tinygrad.codegen import to_program
|
|
from tinygrad.dtype import DType, dtypes, AddrSpace
|
|
from tinygrad.renderer.isa import ISARenderer
|
|
from tinygrad.renderer.cstyle import ClangRenderer
|
|
from tinygrad.renderer.llvmir import AMDLLVMRenderer
|
|
from tinygrad.helpers import Target
|
|
from test.helpers import replace_opts
|
|
|
|
@unittest.skipIf(isinstance(Device[Device.DEFAULT].renderer, ISARenderer), "isa backends don't preserve the op spec when lowering")
|
|
class TestLinearizer(unittest.TestCase):
|
|
def test_load_dedup(self):
|
|
# for different leaves in the AST, the same loads may occur.
|
|
|
|
a = Tensor.randn(4).realize()
|
|
# these are of size 3 to avoid float4 coalesce
|
|
r = a[:-1] + a[1:]
|
|
|
|
uops = tuple(to_program(replace_opts(r.schedule_linear().src[-1].src[0], [Opt(op=OptOps.SPLIT, axis=0, arg=(0, AxisType.UPCAST))]),
|
|
renderer=Device[Device.DEFAULT].renderer).src[1].src)
|
|
num_loads = len([uop for uop in uops if uop.op is Ops.LOAD])
|
|
assert num_loads <= 4, "more load uops than needed"
|
|
assert num_loads >= 1, "expected at least one load uop"
|
|
|
|
@unittest.skip("this is handled at higher level now")
|
|
def test_upcast_cse(self):
|
|
# when upcasting, within a subtree, there may be common expressions.
|
|
|
|
a, b = Tensor.randn(1).realize(), Tensor.randn(1).realize()
|
|
r = a.expand([2]) + b.expand([2])
|
|
|
|
uops = tuple(to_program(replace_opts(r.schedule_linear().src[-1].src[0], [Opt(op=OptOps.SPLIT, axis=0, arg=(0, AxisType.UPCAST))]),
|
|
renderer=Device[Device.DEFAULT].renderer).src[1].src)
|
|
num_ops = len([uop for uop in uops if uop.op in GroupOp.ALU])
|
|
assert num_ops <= 1, "more alu uops than needed"
|
|
|
|
@unittest.skipUnless(Device[Device.DEFAULT].renderer.supports_float4, "test requires float4")
|
|
def test_reduce_upcast(self):
|
|
x, w = Tensor.randn((1,1,3)).realize(), Tensor.randn((1,1,2)).realize()
|
|
r = Tensor.conv2d(x,w,padding=1).relu()
|
|
|
|
uops = tuple(to_program(replace_opts(r.schedule_linear().src[-1].src[0],
|
|
[Opt(op=OptOps.SPLIT, axis=0, arg=(0, AxisType.UPCAST)),
|
|
Opt(op=OptOps.SPLIT, axis=1, arg=(0, AxisType.UPCAST))]), renderer=Device[Device.DEFAULT].renderer).src[1].src)
|
|
accs = [u for u in uops if u.op is Ops.BUFFER and u.addrspace is AddrSpace.REG]
|
|
stores = [u for u in uops if u.op is Ops.STORE]
|
|
assert len(accs) == 0 # it's removed now
|
|
assert len(stores) == 1
|
|
|
|
def test_zero_fold(self):
|
|
a, b = Tensor.randn(1).realize(), Tensor.randn(1).realize()
|
|
r = Tensor.stack(a, b)
|
|
uops = tuple(to_program(replace_opts(r.schedule_linear().src[-1].src[0], [Opt(op=OptOps.SPLIT, axis=0, arg=(0, AxisType.UPCAST))]),
|
|
renderer=Device[Device.DEFAULT].renderer).src[1].src)
|
|
num_ops = len([uop for uop in uops if uop.op in GroupOp.ALU])
|
|
assert num_ops == 0, "more alu uops than needed"
|
|
|
|
def test_sum_acc_dtype(self):
|
|
for tensor_dtype, acc_dtype in (
|
|
(dtypes.bool, dtypes.int), (dtypes.int16, dtypes.int), (dtypes.float16, dtypes.float), (dtypes.bfloat16, dtypes.float)):
|
|
if tensor_dtype in (dts:=Device[Device.DEFAULT].renderer.supported_dtypes()) and acc_dtype in dts:
|
|
a = Tensor([1, 2, 3], dtype=tensor_dtype).sum()
|
|
realized_ast = a.schedule_linear().src[-1].src[0]
|
|
program = to_program(replace_opts(realized_ast, []), renderer=Device[Device.DEFAULT].renderer)
|
|
local = [uop for uop in tuple(program.src[1].src) if uop.op is Ops.BUFFER and uop.addrspace in (AddrSpace.LOCAL, AddrSpace.REG)]
|
|
assert local[0].dtype == acc_dtype
|
|
|
|
def test_arg_acc_dtype(self):
|
|
def helper_arg_acc_dtype(c: Tensor, expected_dtype:DType):
|
|
realized_ast = c.schedule_linear().src[-1].src[0]
|
|
program = to_program(replace_opts(realized_ast, []), renderer=Device[Device.DEFAULT].renderer)
|
|
local = [uop for uop in tuple(program.src[1].src) if uop.op is Ops.BUFFER and uop.addrspace in (AddrSpace.LOCAL, AddrSpace.REG)]
|
|
self.assertEqual(local[0].dtype, expected_dtype)
|
|
|
|
tests = (
|
|
(dtypes.float16, None, dtypes.float),
|
|
(dtypes.bfloat16, None, dtypes.float),
|
|
(dtypes.float, None, dtypes.float),
|
|
(dtypes.float16, dtypes.float16, dtypes.float16),
|
|
(dtypes.bfloat16, dtypes.bfloat16, dtypes.bfloat16),
|
|
(dtypes.float, dtypes.float16, dtypes.float16),
|
|
)
|
|
for tensor_dtype, acc_dtype, expected_dtype in tests:
|
|
if tensor_dtype in (dts:=Device[Device.DEFAULT].renderer.supported_dtypes()) and acc_dtype in dts|{None} and expected_dtype in dts:
|
|
a, b = Tensor.rand(8, 8, dtype=tensor_dtype), Tensor.rand(8, 8, dtype=tensor_dtype)
|
|
helper_arg_acc_dtype(a.sum(dtype=acc_dtype), expected_dtype)
|
|
helper_arg_acc_dtype(a.matmul(b, dtype=acc_dtype), expected_dtype)
|
|
helper_arg_acc_dtype(Tensor.einsum("ki,ij->kj", a, b, dtype=acc_dtype), expected_dtype)
|
|
d, w = Tensor.rand(4, 8, 8, 8, dtype=tensor_dtype), Tensor.rand(8, 8, 2, 2, dtype=tensor_dtype)
|
|
helper_arg_acc_dtype(d.conv2d(w, dtype=acc_dtype), expected_dtype)
|
|
|
|
def test_sum_collapse(self):
|
|
t = Tensor([2]).reshape(1, 1).expand(256, 256).sum()
|
|
sched = [si for si in t.schedule_linear().src if si.src[0].op is Ops.SINK]
|
|
# sum_collapse is a full collapse now
|
|
assert len(sched) == 1
|
|
assert not any(u.op is Ops.REDUCE and u.arg[1] > 0 for u in sched[0].src[0].toposort()), "found reduce in sum collapse"
|
|
|
|
class TestLinearizerRenderers(unittest.TestCase):
|
|
# NOTE: can reenable, it does work. it just makes BEAM slow
|
|
@unittest.expectedFailure
|
|
def test_upcast_with_locals_cpu(self):
|
|
out = Tensor.ones(64,64).contiguous() @ Tensor.ones(64,64).contiguous()
|
|
prg = to_program(replace_opts(out.schedule_linear().src[-1].src[0], [Opt(OptOps.SPLIT, axis=0, arg=(4, AxisType.LOCAL))]),
|
|
renderer=ClangRenderer(Target("CPU", arch="x86_64,x86-64")))
|
|
self.assertEqual(len(prg.src[2].arg.split("for")), 5)
|
|
|
|
def test_upcast_with_locals(self):
|
|
x, y = Tensor.rand(1,128), Tensor.rand(128, 128)
|
|
r = (x@y).relu()
|
|
opts_to_apply = [Opt(op=OptOps.SPLIT, axis=1, arg=(8, AxisType.LOCAL)), Opt(op=OptOps.SPLIT, axis=0, arg=(4, AxisType.LOCAL)),
|
|
Opt(op=OptOps.SPLIT, axis=0, arg=(4, AxisType.UPCAST))]
|
|
program = to_program(replace_opts(r.schedule_linear().src[-1].src[0], opts_to_apply),
|
|
renderer=AMDLLVMRenderer(Target("AMD", arch="gfx1100")))
|
|
|
|
stores = [u for u in tuple(program.src[1].src) if u.op is Ops.STORE and u.src[0].addrspace != AddrSpace.REG]
|
|
|
|
# the first store is to lds and can be upcasted
|
|
assert stores[0].src[1].max_numel() == 4
|
|
assert any(x.addrspace is AddrSpace.LOCAL for x in stores[0].toposort())
|
|
# the second store is to gds with no upcasts
|
|
assert stores[1].src[1].max_numel() == 1
|
|
assert stores[1].src[1].dtype == dtypes.float
|
|
assert any(x.op is Ops.PARAM for x in stores[1].toposort())
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|