blob: c28b8c444bef29e688a4abeef23b038e4448e341 [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ruff: noqa: E501, F401, F841
"""Integration test for MetaSchedule"""
import tempfile
import numpy as np
import pytest
import tvm
import tvm.testing
from tvm import relax
from tvm.runtime import tensor as tvm_tensor
from tvm.runtime import cpu as tvm_cpu
from tvm.runtime.vm import VirtualMachine
from tvm.s_tir import meta_schedule as ms
from tvm.script import ir as I
from tvm.script import relax as R
from tvm.script import tirx as T
# fmt: off
@I.ir_module
class Module0:
@R.function
def main(data: R.Tensor((1, 8, 8, 4), dtype="int32")) -> R.Tensor((1, 8, 8, 4), dtype="int32"):
cls = Module0
with R.dataflow():
c = R.const([[[[-171701247],[-1719837685],[1801664104],[-634316588]],[[920159370],[-132073802],[2142531563],[1465185701]],[[-1505608067],[1737948828],[1581089391],[-1986167320]]],[[[-1449581822],[35714587],[496324563],[-1430879015]],[[-1615680873],[1198514997],[1494683955],[1567376558]],[[1319924884],[-380548171],[296785437],[-1546305981]]],[[[-398644701],[-2004794585],[-1850413687],[2072643657]],[[847950121],[-544212073],[-199532669],[-343273682]],[[953721562],[-1930209358],[1573600108],[-577689853]]]], "int32")
lv: R.Tensor((1, 8, 8, 4), dtype="int32") = R.nn.conv2d(data, c, strides=[1, 1], padding=[1, 1, 1, 1], dilation=[1, 1], groups=4, data_layout="NHWC", kernel_layout="HWOI", out_layout="NHWC", out_dtype="int32")
b = R.const([[[[1, 1, 1, 1]]]], "int32")
lv1: R.Tensor((1, 8, 8, 4), dtype="int32") = R.add(lv, b)
c1 = R.const([[[[2042349344],[-2076067063],[1528163722],[-1156452837]],[[-2097172051],[1137787079],[-601389657],[1907495997]],[[987801941],[1073738593],[-1410339796],[-689755358]]],[[[90351522],[-44886952],[-1914103775],[-691553659]],[[-1288505112],[-1376578817],[-2067933148],[-1413101824]],[[1261422027],[-156976862],[-1185734459],[1608778622]]],[[[-664209483],[1907479806],[1838595152],[464942526]],[[877953160],[415131837],[-2010736511],[1218242769]],[[-1440127632],[112931],[521745784],[-1931145893]]]], "int32")
lv2: R.Tensor((1, 8, 8, 4), dtype="int32") = R.nn.conv2d(lv1, c1, strides=[1, 1], padding=[1, 1, 1, 1], dilation=[1, 1], groups=4, data_layout="NHWC", kernel_layout="HWOI", out_layout="NHWC", out_dtype="int32")
c2 = R.const([[[[687940110],[-910571705],[-901609800],[-500525928]],[[506872399],[1070176297],[-305936110],[1625439784]],[[-1565626954],[-1705688881],[-866370805],[-1750740826]]],[[[300497007],[-626864803],[390295545],[222549121]],[[319224543],[-2003064970],[657992492],[2014175448]],[[653278589],[-768810984],[-294555581],[-1197167662]]],[[[1703154671],[-1540759805],[-568817430],[-1729755444]],[[-275458074],[2078945571],[1683298006],[-1029327874]],[[1315093181],[159010501],[875694807],[-223655381]]]], "int32")
lv3: R.Tensor((1, 8, 8, 4), dtype="int32") = R.nn.conv2d(lv2, c2, strides=[1, 1], padding=[1, 1, 1, 1], dilation=[1, 1], groups=4, data_layout="NHWC", kernel_layout="HWOI", out_layout="NHWC", out_dtype="int32")
gv: R.Tensor((1, 8, 8, 4), dtype="int32") = lv3
R.output(gv)
return gv
# fmt: on
def test_extracting_tasks():
target = {"kind": "llvm", "mcpu": "core-avx2", "num-cores": 1}
relax_mod = Module0
relax_mod = relax.transform.LegalizeOps()(relax_mod)
relax_mod = relax.transform.AnnotateTIROpPattern()(relax_mod)
relax_mod = relax.transform.FuseOps()(relax_mod)
relax_mod = relax.transform.FoldConstant()(relax_mod)
relax_mod = relax.transform.FuseTIR()(relax_mod)
relax_expectation = {
"structural": 2, # The relax constants do not reach the tirx at the lowering.
"ignore-tensor": 2,
"anchor-block": 1,
}
for module_equality, count in relax_expectation.items():
extracted_tasks = ms.relax_integration.extract_tasks(
relax_mod,
target,
{},
module_equality=module_equality,
)
assert len(extracted_tasks) == count
def test_compile_relax_with_database():
"""End-to-end test: tune with MetaSchedule then compile_relax with the database.
Verifies that the pipeline ordering in compile_relax is correct: tasks are
extracted and tuned against fused-TIR keys, and compile_relax produces those
same keys (by running LegalizeOps + FuseOps + FuseTIR before applying the
database), so the scheduled kernels are actually picked up.
"""
target = tvm.target.Target({"kind": "llvm", "num-cores": 1})
# Prepare the fused module whose TIR keys will populate the database.
fused_mod = Module0
fused_mod = relax.transform.LegalizeOps()(fused_mod)
fused_mod = relax.transform.AnnotateTIROpPattern()(fused_mod)
fused_mod = relax.transform.FuseOps()(fused_mod)
fused_mod = relax.transform.FoldConstant()(fused_mod)
fused_mod = relax.transform.FuseTIR()(fused_mod)
with tempfile.TemporaryDirectory() as work_dir:
database = ms.relax_integration.tune_relax(
fused_mod,
params={},
target=target,
work_dir=work_dir,
max_trials_global=4,
)
# compile_relax takes the raw module and builds the fused-TIR pipeline
# internally; the database keys must therefore match the ones above.
exe = ms.relax_integration.compile_relax(
database=database,
mod=Module0,
target=target,
params=None,
)
dev = tvm_cpu()
vm = VirtualMachine(exe.jit(), dev)
data = tvm_tensor(np.zeros((1, 8, 8, 4), dtype="int32"), device=dev)
result = vm["main"](data)
assert result.numpy().shape == (1, 8, 8, 4)
if __name__ == "__main__":
tvm.testing.main()