830 lines
23 KiB
Python
830 lines
23 KiB
Python
# 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.
|
|
|
|
import tvm
|
|
import tvm.testing
|
|
from tvm import tirx as tir
|
|
from tvm.ir import Call, Op
|
|
from tvm.ir.base import assert_structural_equal
|
|
from tvm.tirx.expr import (
|
|
EQ,
|
|
GE,
|
|
GT,
|
|
LE,
|
|
LT,
|
|
NE,
|
|
Add,
|
|
And,
|
|
Broadcast,
|
|
BufferLoad,
|
|
Cast,
|
|
Div,
|
|
FloatImm,
|
|
FloorDiv,
|
|
FloorMod,
|
|
IntImm,
|
|
Let,
|
|
Max,
|
|
Min,
|
|
Mod,
|
|
Mul,
|
|
Not,
|
|
Or,
|
|
ProducerLoad,
|
|
Ramp,
|
|
Reduce,
|
|
Select,
|
|
Shuffle,
|
|
StringImm,
|
|
Sub,
|
|
Var,
|
|
)
|
|
from tvm.tirx.expr_functor import ExprMutator, ExprVisitor
|
|
|
|
# Basic example variables for testing
|
|
n = tir.Var("n", "int32")
|
|
m = tir.Var("m", "int32")
|
|
x = tir.Var("x", "float32")
|
|
y = tir.Var("y", "float32")
|
|
|
|
|
|
class BasicVisitor(ExprVisitor):
|
|
"""Default ExprVisitor"""
|
|
|
|
|
|
class ASTLog:
|
|
"""Helper class to log AST"""
|
|
|
|
def __init__(self) -> None:
|
|
self.log = []
|
|
self.indent = "\t"
|
|
self.level = 0
|
|
|
|
def push_scope(self):
|
|
self.level += 1
|
|
|
|
def pop_scope(self):
|
|
self.level -= 1
|
|
|
|
def add(self, s: str):
|
|
self.log.append(self.indent * self.level + s)
|
|
|
|
def __str__(self) -> str:
|
|
return "\n".join(self.log)
|
|
|
|
|
|
class ASTPrinter(ExprVisitor):
|
|
"""Print TIR AST in structured format."""
|
|
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self.log = ASTLog()
|
|
|
|
def visit_var_(self, op: Var) -> None:
|
|
self.log.add("Var")
|
|
|
|
def visit_buffer_load_(self, op: BufferLoad) -> None:
|
|
self.log.add("BufferLoad")
|
|
self.log.push_scope()
|
|
for idx in op.indices:
|
|
self.visit_expr(idx)
|
|
self.log.pop_scope()
|
|
|
|
def visit_producer_load_(self, op: ProducerLoad) -> None:
|
|
self.log.add("ProducerLoad")
|
|
self.log.push_scope()
|
|
for idx in op.indices:
|
|
self.visit_expr(idx)
|
|
self.log.pop_scope()
|
|
|
|
def visit_let_(self, op: Let) -> None:
|
|
self.log.add("Let")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.var)
|
|
self.visit_expr(op.value)
|
|
self.visit_expr(op.body)
|
|
self.log.pop_scope()
|
|
|
|
def visit_call_(self, op: Call) -> None:
|
|
self.log.add("Call")
|
|
self.log.push_scope()
|
|
if isinstance(op.op, Op):
|
|
self.log.add("Op")
|
|
else:
|
|
self.visit_expr(op.op)
|
|
for arg in op.args:
|
|
self.visit_expr(arg)
|
|
self.log.pop_scope()
|
|
|
|
def visit_add_(self, op: Add) -> None:
|
|
self.log.add("Add")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
def visit_sub_(self, op: Sub) -> None:
|
|
self.log.add("Sub")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
def visit_mul_(self, op: Mul) -> None:
|
|
self.log.add("Mul")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
def visit_div_(self, op: Div) -> None:
|
|
self.log.add("Div")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
def visit_mod_(self, op: Mod) -> None:
|
|
self.log.add("Mod")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
def visit_floordiv_(self, op: FloorDiv) -> None:
|
|
self.log.add("FloorDiv")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
def visit_floormod_(self, op: FloorMod) -> None:
|
|
self.log.add("FloorMod")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
def visit_min_(self, op: Min) -> None:
|
|
self.log.add("Min")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
def visit_max_(self, op: Max) -> None:
|
|
self.log.add("Max")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
def visit_eq_(self, op: EQ) -> None:
|
|
self.log.add("EQ")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
def visit_ne_(self, op: NE) -> None:
|
|
self.log.add("NE")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
def visit_lt_(self, op: LT) -> None:
|
|
self.log.add("LT")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
def visit_le_(self, op: LE) -> None:
|
|
self.log.add("LE")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
def visit_gt_(self, op: GT) -> None:
|
|
self.log.add("GT")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
def visit_ge_(self, op: GE) -> None:
|
|
self.log.add("GE")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
def visit_and_(self, op: And) -> None:
|
|
self.log.add("And")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
def visit_or_(self, op: Or) -> None:
|
|
self.log.add("Or")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
def visit_reduce_(self, op: Reduce) -> None:
|
|
self.log.add("Reduce")
|
|
self.log.push_scope()
|
|
for source in op.source:
|
|
self.visit_expr(source)
|
|
for axis in op.axis:
|
|
self.visit_expr(axis.var)
|
|
self.visit_expr(op.condition)
|
|
self.log.pop_scope()
|
|
|
|
def visit_cast_(self, op: Cast) -> None:
|
|
self.log.add("Cast")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.value)
|
|
self.log.pop_scope()
|
|
|
|
def visit_not_(self, op: Not) -> None:
|
|
self.log.add("Not")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.log.pop_scope()
|
|
|
|
def visit_select_(self, op: Select) -> None:
|
|
self.log.add("Select")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.condition)
|
|
self.visit_expr(op.true_value)
|
|
self.visit_expr(op.false_value)
|
|
self.log.pop_scope()
|
|
|
|
def visit_ramp_(self, op: Ramp) -> None:
|
|
self.log.add("Ramp")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.base)
|
|
self.visit_expr(op.stride)
|
|
self.visit_expr(op.lanes)
|
|
self.log.pop_scope()
|
|
|
|
def visit_broadcast_(self, op: Broadcast) -> None:
|
|
self.log.add("Broadcast")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.value)
|
|
self.visit_expr(op.lanes)
|
|
self.log.pop_scope()
|
|
|
|
def visit_shuffle_(self, op: Shuffle) -> None:
|
|
self.log.add("Shuffle")
|
|
self.log.push_scope()
|
|
for vec in op.vectors:
|
|
self.visit_expr(vec)
|
|
for idx in op.indices:
|
|
self.visit_expr(idx)
|
|
self.log.pop_scope()
|
|
|
|
def visit_int_imm_(self, op: IntImm) -> None:
|
|
self.log.add("IntImm")
|
|
|
|
def visit_float_imm_(self, op: FloatImm) -> None:
|
|
self.log.add("FloatImm")
|
|
|
|
def visit_string_imm_(self, op: StringImm) -> None:
|
|
self.log.add("StringImm")
|
|
|
|
|
|
class BasicMutator(ExprMutator):
|
|
"""Default ExprMutator"""
|
|
|
|
|
|
class ASTPostPrinterMutator(ExprMutator):
|
|
"""Print TIR AST in the post order format."""
|
|
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self.log = ASTLog()
|
|
|
|
def visit_var_(self, op: Var) -> tir.Expr:
|
|
result = super().visit_var_(op)
|
|
self.log.add("Var")
|
|
return result
|
|
|
|
def visit_buffer_load_(self, op: BufferLoad) -> tir.Expr:
|
|
result = super().visit_buffer_load_(op)
|
|
self.log.add("BufferLoad")
|
|
return result
|
|
|
|
def visit_producer_load_(self, op: ProducerLoad) -> tir.Expr:
|
|
result = super().visit_producer_load_(op)
|
|
self.log.add("ProducerLoad")
|
|
return result
|
|
|
|
def visit_let_(self, op: Let) -> tir.Expr:
|
|
result = super().visit_let_(op)
|
|
self.log.add("Let")
|
|
return result
|
|
|
|
def visit_call_(self, op: Call) -> tir.Expr:
|
|
result = super().visit_call_(op)
|
|
self.log.add("Call")
|
|
return result
|
|
|
|
def visit_add_(self, op: Add) -> tir.Expr:
|
|
result = super().visit_add_(op)
|
|
self.log.add("Add")
|
|
return result
|
|
|
|
def visit_sub_(self, op: Sub) -> tir.Expr:
|
|
result = super().visit_sub_(op)
|
|
self.log.add("Sub")
|
|
return result
|
|
|
|
def visit_mul_(self, op: Mul) -> tir.Expr:
|
|
result = super().visit_mul_(op)
|
|
self.log.add("Mul")
|
|
return result
|
|
|
|
def visit_div_(self, op: Div) -> tir.Expr:
|
|
result = super().visit_div_(op)
|
|
self.log.add("Div")
|
|
return result
|
|
|
|
def visit_mod_(self, op: Mod) -> tir.Expr:
|
|
result = super().visit_mod_(op)
|
|
self.log.add("Mod")
|
|
return result
|
|
|
|
def visit_floordiv_(self, op: FloorDiv) -> tir.Expr:
|
|
result = super().visit_floordiv_(op)
|
|
self.log.add("FloorDiv")
|
|
return result
|
|
|
|
def visit_floormod_(self, op: FloorMod) -> tir.Expr:
|
|
result = super().visit_floormod_(op)
|
|
self.log.add("FloorMod")
|
|
return result
|
|
|
|
def visit_min_(self, op: Min) -> tir.Expr:
|
|
result = super().visit_min_(op)
|
|
self.log.add("Min")
|
|
return result
|
|
|
|
def visit_max_(self, op: Max) -> tir.Expr:
|
|
result = super().visit_max_(op)
|
|
self.log.add("Max")
|
|
return result
|
|
|
|
def visit_eq_(self, op: EQ) -> tir.Expr:
|
|
result = super().visit_eq_(op)
|
|
self.log.add("EQ")
|
|
return result
|
|
|
|
def visit_ne_(self, op: NE) -> tir.Expr:
|
|
result = super().visit_ne_(op)
|
|
self.log.add("NE")
|
|
return result
|
|
|
|
def visit_lt_(self, op: LT) -> tir.Expr:
|
|
result = super().visit_lt_(op)
|
|
self.log.add("LT")
|
|
return result
|
|
|
|
def visit_le_(self, op: LE) -> tir.Expr:
|
|
result = super().visit_le_(op)
|
|
self.log.add("LE")
|
|
return result
|
|
|
|
def visit_gt_(self, op: GT) -> tir.Expr:
|
|
result = super().visit_gt_(op)
|
|
self.log.add("GT")
|
|
return result
|
|
|
|
def visit_ge_(self, op: GE) -> tir.Expr:
|
|
result = super().visit_ge_(op)
|
|
self.log.add("GE")
|
|
return result
|
|
|
|
def visit_and_(self, op: And) -> tir.Expr:
|
|
result = super().visit_and_(op)
|
|
self.log.add("And")
|
|
return result
|
|
|
|
def visit_or_(self, op: Or) -> tir.Expr:
|
|
result = super().visit_or_(op)
|
|
self.log.add("Or")
|
|
return result
|
|
|
|
def visit_reduce_(self, op: Reduce) -> tir.Expr:
|
|
result = super().visit_reduce_(op)
|
|
self.log.add("Reduce")
|
|
return result
|
|
|
|
def visit_cast_(self, op: Cast) -> tir.Expr:
|
|
result = super().visit_cast_(op)
|
|
self.log.add("Cast")
|
|
return result
|
|
|
|
def visit_not_(self, op: Not) -> tir.Expr:
|
|
result = super().visit_not_(op)
|
|
self.log.add("Not")
|
|
return result
|
|
|
|
def visit_select_(self, op: Select) -> tir.Expr:
|
|
result = super().visit_select_(op)
|
|
self.log.add("Select")
|
|
return result
|
|
|
|
def visit_ramp_(self, op: Ramp) -> tir.Expr:
|
|
result = super().visit_ramp_(op)
|
|
self.log.add("Ramp")
|
|
return result
|
|
|
|
def visit_broadcast_(self, op: Broadcast) -> tir.Expr:
|
|
result = super().visit_broadcast_(op)
|
|
self.log.add("Broadcast")
|
|
return result
|
|
|
|
def visit_shuffle_(self, op: Shuffle) -> tir.Expr:
|
|
result = super().visit_shuffle_(op)
|
|
self.log.add("Shuffle")
|
|
return result
|
|
|
|
def visit_int_imm_(self, op: IntImm) -> tir.Expr:
|
|
result = super().visit_int_imm_(op)
|
|
self.log.add("IntImm")
|
|
return result
|
|
|
|
def visit_float_imm_(self, op: FloatImm) -> tir.Expr:
|
|
result = super().visit_float_imm_(op)
|
|
self.log.add("FloatImm")
|
|
return result
|
|
|
|
def visit_string_imm_(self, op: StringImm) -> tir.Expr:
|
|
result = super().visit_string_imm_(op)
|
|
self.log.add("StringImm")
|
|
return result
|
|
|
|
|
|
def basic_check(expr, visitor_str, mutator_str):
|
|
"""Helper function to check visitor and mutator on an expression"""
|
|
|
|
# Check visitor
|
|
basic_visitor = BasicVisitor()
|
|
basic_visitor.visit_expr(expr)
|
|
# Check AST printer visitor
|
|
log_visitor = ASTPrinter()
|
|
log_visitor.visit_expr(expr)
|
|
assert str(log_visitor.log) == visitor_str
|
|
|
|
# Check basic mutator
|
|
basic_mutator = BasicMutator()
|
|
mutated_expr = basic_mutator.visit_expr(expr)
|
|
assert_structural_equal(mutated_expr, expr)
|
|
|
|
# Check post-order printer mutator
|
|
post_log_mutator = ASTPostPrinterMutator()
|
|
mutated_expr = post_log_mutator.visit_expr(expr)
|
|
assert_structural_equal(mutated_expr, expr)
|
|
assert str(post_log_mutator.log) == mutator_str
|
|
|
|
|
|
def test_var():
|
|
basic_check(n, "Var", "Var")
|
|
|
|
|
|
def test_int_imm():
|
|
basic_check(tir.IntImm("int32", 10), "IntImm", "IntImm")
|
|
|
|
|
|
def test_float_imm():
|
|
basic_check(tir.FloatImm("float32", 1.5), "FloatImm", "FloatImm")
|
|
|
|
|
|
def test_string_imm():
|
|
basic_check(tir.StringImm("hello"), "StringImm", "StringImm")
|
|
|
|
|
|
def test_add():
|
|
add_node = tir.Add(n, m)
|
|
basic_check(add_node, "\n".join(["Add", "\tVar", "\tVar"]), "\n".join(["Var", "Var", "Add"]))
|
|
|
|
|
|
def test_sub():
|
|
sub_node = tir.Sub(n, m)
|
|
basic_check(sub_node, "\n".join(["Sub", "\tVar", "\tVar"]), "\n".join(["Var", "Var", "Sub"]))
|
|
|
|
|
|
def test_mul():
|
|
mul_node = tir.Mul(n, m)
|
|
basic_check(mul_node, "\n".join(["Mul", "\tVar", "\tVar"]), "\n".join(["Var", "Var", "Mul"]))
|
|
|
|
|
|
def test_div():
|
|
div_node = tir.Div(n, m)
|
|
basic_check(div_node, "\n".join(["Div", "\tVar", "\tVar"]), "\n".join(["Var", "Var", "Div"]))
|
|
|
|
|
|
def test_floor_div():
|
|
floor_div_node = tir.FloorDiv(n, m)
|
|
basic_check(
|
|
floor_div_node,
|
|
"\n".join(["FloorDiv", "\tVar", "\tVar"]),
|
|
"\n".join(["Var", "Var", "FloorDiv"]),
|
|
)
|
|
|
|
|
|
def test_floor_mod():
|
|
floor_mod_node = tir.FloorMod(n, m)
|
|
basic_check(
|
|
floor_mod_node,
|
|
"\n".join(["FloorMod", "\tVar", "\tVar"]),
|
|
"\n".join(["Var", "Var", "FloorMod"]),
|
|
)
|
|
|
|
|
|
def test_min():
|
|
min_node = tir.Min(n, m)
|
|
basic_check(min_node, "\n".join(["Min", "\tVar", "\tVar"]), "\n".join(["Var", "Var", "Min"]))
|
|
|
|
|
|
def test_max():
|
|
max_node = tir.Max(n, m)
|
|
basic_check(max_node, "\n".join(["Max", "\tVar", "\tVar"]), "\n".join(["Var", "Var", "Max"]))
|
|
|
|
|
|
def test_eq():
|
|
eq_node = tir.EQ(n, m)
|
|
basic_check(eq_node, "\n".join(["EQ", "\tVar", "\tVar"]), "\n".join(["Var", "Var", "EQ"]))
|
|
|
|
|
|
def test_ne():
|
|
ne_node = tir.NE(n, m)
|
|
basic_check(ne_node, "\n".join(["NE", "\tVar", "\tVar"]), "\n".join(["Var", "Var", "NE"]))
|
|
|
|
|
|
def test_lt():
|
|
lt_node = tir.LT(n, m)
|
|
basic_check(lt_node, "\n".join(["LT", "\tVar", "\tVar"]), "\n".join(["Var", "Var", "LT"]))
|
|
|
|
|
|
def test_le():
|
|
le_node = tir.LE(n, m)
|
|
basic_check(le_node, "\n".join(["LE", "\tVar", "\tVar"]), "\n".join(["Var", "Var", "LE"]))
|
|
|
|
|
|
def test_gt():
|
|
gt_node = tir.GT(n, m)
|
|
basic_check(gt_node, "\n".join(["GT", "\tVar", "\tVar"]), "\n".join(["Var", "Var", "GT"]))
|
|
|
|
|
|
def test_ge():
|
|
ge_node = tir.GE(n, m)
|
|
basic_check(ge_node, "\n".join(["GE", "\tVar", "\tVar"]), "\n".join(["Var", "Var", "GE"]))
|
|
|
|
|
|
def test_and():
|
|
and_node = tir.And(tir.EQ(n, m), tir.LT(n, 10))
|
|
basic_check(
|
|
and_node,
|
|
"\n".join(["And", "\tEQ", "\t\tVar", "\t\tVar", "\tLT", "\t\tVar", "\t\tIntImm"]),
|
|
"\n".join(["Var", "Var", "EQ", "Var", "IntImm", "LT", "And"]),
|
|
)
|
|
|
|
|
|
def test_or():
|
|
or_node = tir.Or(tir.EQ(n, m), tir.LT(n, 10))
|
|
basic_check(
|
|
or_node,
|
|
"\n".join(["Or", "\tEQ", "\t\tVar", "\t\tVar", "\tLT", "\t\tVar", "\t\tIntImm"]),
|
|
"\n".join(["Var", "Var", "EQ", "Var", "IntImm", "LT", "Or"]),
|
|
)
|
|
|
|
|
|
def test_not():
|
|
not_node = tir.Not(tir.EQ(n, m))
|
|
basic_check(
|
|
not_node,
|
|
"\n".join(["Not", "\tEQ", "\t\tVar", "\t\tVar"]),
|
|
"\n".join(["Var", "Var", "EQ", "Not"]),
|
|
)
|
|
|
|
|
|
def test_select():
|
|
select_node = tir.Select(tir.EQ(n, m), n, m)
|
|
basic_check(
|
|
select_node,
|
|
"\n".join(["Select", "\tEQ", "\t\tVar", "\t\tVar", "\tVar", "\tVar"]),
|
|
"\n".join(["Var", "Var", "EQ", "Var", "Var", "Select"]),
|
|
)
|
|
|
|
|
|
def test_cast():
|
|
cast_node = tir.Cast("float32", n)
|
|
basic_check(cast_node, "\n".join(["Cast", "\tVar"]), "\n".join(["Var", "Cast"]))
|
|
|
|
|
|
def test_let():
|
|
let_node = tir.Let(n, tir.IntImm("int32", 10), n + 1)
|
|
basic_check(
|
|
let_node,
|
|
"\n".join(["Let", "\tVar", "\tIntImm", "\tAdd", "\t\tVar", "\t\tIntImm"]),
|
|
"\n".join(["Var", "IntImm", "Var", "IntImm", "Add", "Let"]),
|
|
)
|
|
|
|
|
|
def test_ramp():
|
|
ramp_node = tir.Ramp(n, 1, 4)
|
|
basic_check(
|
|
ramp_node,
|
|
"\n".join(["Ramp", "\tVar", "\tIntImm", "\tIntImm"]),
|
|
"\n".join(["Var", "IntImm", "IntImm", "Ramp"]),
|
|
)
|
|
|
|
|
|
def test_broadcast():
|
|
broadcast_node = tir.Broadcast(n, 4)
|
|
basic_check(
|
|
broadcast_node,
|
|
"\n".join(["Broadcast", "\tVar", "\tIntImm"]),
|
|
"\n".join(["Var", "IntImm", "Broadcast"]),
|
|
)
|
|
|
|
|
|
def test_inherit():
|
|
# The internal class is not instantiated.
|
|
class InternalVisitor(ExprVisitor):
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self.log = ASTLog()
|
|
|
|
def visit_add_(self, op: Add) -> None:
|
|
self.log.add("InternalAdd")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
def visit_var_(self, op: Var) -> None:
|
|
self.log.add("InternalVar")
|
|
|
|
class LeafVisitor(InternalVisitor):
|
|
def visit_add_(self, op: Add) -> None:
|
|
self.log.add("LeafAdd")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
add_node = tir.Add(n, m)
|
|
lv = LeafVisitor()
|
|
lv.visit_expr(add_node)
|
|
assert str(lv.log) == "\n".join(["LeafAdd", "\tInternalVar", "\tInternalVar"])
|
|
|
|
|
|
def test_inherit_with_cls():
|
|
class InternalVisitor(ExprVisitor):
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self.log = ASTLog()
|
|
|
|
def visit_add_(self, op: Add) -> None:
|
|
self.log.add("InternalAdd")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
def visit_var_(self, op: Var) -> None:
|
|
self.log.add("InternalVar")
|
|
|
|
class LeafVisitor(InternalVisitor):
|
|
def visit_add_(self, op: Add) -> None:
|
|
self.log.add("LeafAdd")
|
|
self.log.push_scope()
|
|
self.visit_expr(op.a)
|
|
self.visit_expr(op.b)
|
|
self.log.pop_scope()
|
|
|
|
add_node = tir.Add(n, m)
|
|
iv = InternalVisitor()
|
|
iv.visit_expr(add_node)
|
|
assert str(iv.log) == "\n".join(["InternalAdd", "\tInternalVar", "\tInternalVar"])
|
|
|
|
lv = LeafVisitor()
|
|
lv.visit_expr(add_node)
|
|
assert str(lv.log) == "\n".join(["LeafAdd", "\tInternalVar", "\tInternalVar"])
|
|
|
|
|
|
def test_call_visitor_super():
|
|
class InternalVisitor(ExprVisitor):
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self.log = ASTLog()
|
|
|
|
def visit_add_(self, op: Add) -> None:
|
|
self.log.add("InternalAdd")
|
|
super().visit_add_(op) # call ExprVisitor.visit_add_
|
|
|
|
def visit_var_(self, op: Var) -> None:
|
|
self.log.add("InternalVar")
|
|
|
|
def visit_int_imm_(self, op: IntImm) -> None:
|
|
self.log.add("InternalIntImm")
|
|
|
|
class LeafVisitor(InternalVisitor):
|
|
def visit_add_(self, op: Add) -> None:
|
|
self.log.add("LeafAdd")
|
|
super().visit_add_(op) # call InternalVisitor.visit_add_
|
|
|
|
add_node = tir.Add(n, tir.IntImm("int32", 10))
|
|
iv = InternalVisitor()
|
|
iv.visit_expr(add_node)
|
|
assert str(iv.log) == "\n".join(["InternalAdd", "InternalVar", "InternalIntImm"])
|
|
|
|
lv = LeafVisitor()
|
|
lv.visit_expr(add_node)
|
|
assert str(lv.log) == "\n".join(["LeafAdd", "InternalAdd", "InternalVar", "InternalIntImm"])
|
|
|
|
|
|
def test_call_mutator_super():
|
|
class InternalMutator(ExprMutator):
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self.log = ASTLog()
|
|
|
|
def visit_add_(self, op: Add) -> tir.Expr:
|
|
self.log.add("InternalAdd")
|
|
return super().visit_add_(op) # call ExprMutator.visit_add_
|
|
|
|
def visit_var_(self, op: Var) -> tir.Expr:
|
|
self.log.add("InternalVar")
|
|
return super().visit_var_(op) # call ExprMutator.visit_var_
|
|
|
|
def visit_int_imm_(self, op: IntImm) -> tir.Expr:
|
|
self.log.add("InternalIntImm")
|
|
return super().visit_int_imm_(op) # call ExprMutator.visit_int_imm_
|
|
|
|
class LeafMutator(InternalMutator):
|
|
def visit_add_(self, op: Add) -> tir.Expr:
|
|
self.log.add("LeafAdd")
|
|
return super().visit_add_(op) # call InternalMutator.visit_add_
|
|
|
|
add_node = tir.Add(n, tir.IntImm("int32", 10))
|
|
im = InternalMutator()
|
|
im.visit_expr(add_node)
|
|
assert str(im.log) == "\n".join(["InternalAdd", "InternalVar", "InternalIntImm"])
|
|
|
|
lm = LeafMutator()
|
|
lm.visit_expr(add_node)
|
|
assert str(lm.log) == "\n".join(["LeafAdd", "InternalAdd", "InternalVar", "InternalIntImm"])
|
|
|
|
|
|
def test_var_mutation():
|
|
"""Test mutating variables in a TIR expression"""
|
|
|
|
class VarMutator(ExprMutator):
|
|
def __init__(self, var_map):
|
|
super().__init__()
|
|
self.var_map = var_map
|
|
|
|
def visit_var_(self, op: Var) -> tir.Expr:
|
|
if op.name in self.var_map:
|
|
return self.var_map[op.name]
|
|
return op
|
|
|
|
# Create a simple expression
|
|
expr = n + m
|
|
|
|
# Create a mutator that replaces 'n' with a constant
|
|
var_map = {"n": tir.IntImm("int32", 42)}
|
|
mutator = VarMutator(var_map)
|
|
result = mutator.visit_expr(expr)
|
|
|
|
# The result should be 42 + m
|
|
expected = tir.Add(tir.IntImm("int32", 42), m)
|
|
assert_structural_equal(result, expected)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
tvm.testing.main()
|