Files
wehub-resource-sync 26446540fa
Lint / lint (push) Waiting to run
CI / MacOS (push) Waiting to run
CI / Windows (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:36:25 +08:00

99 lines
3.6 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.
# pylint: disable=missing-docstring, invalid-name, unnecessary-comprehension, unused-argument
import tvm
import tvm.testing
from tvm import relax
from tvm.contrib.hexagon import hexagon_unary_ops
def op_replace(call_node, func) -> bool:
if not isinstance(call_node, relax.Call):
return False
call_tir_op = tvm.ir.Op.get("relax.call_tir")
if call_node.op != call_tir_op:
return False
ops = [
"qnn.tanh",
"qnn.sqrt",
"qnn.rsqrt",
"qnn.exp",
"qnn.erf",
"qnn.sigmoid",
"qnn.hardswish",
"qnn.log",
"qnn.abs",
]
if func.attrs["op_attrs"]["op_name"] in ops:
return True
return False
@relax.expr_functor.mutator
class Tanh2TakeReplace(tvm.relax.PyExprMutator):
def __init__(self, mod: tvm.IRModule) -> None:
super().__init__(mod)
self.mod_ = mod
def transform(self) -> tvm.IRModule:
# Iterate over all the nodes to check for the node replaceable
for global_var, func in self.mod_.functions.items():
# Skip non-relax functions
if not isinstance(func, relax.Function):
continue
updated_func = self.visit_expr(func)
self.builder_.normalize(updated_func)
self.builder_.update_func(global_var, updated_func)
# At the end of the transformation we return the updated IRModule from the BlockBuilder.
return self.builder_.get()
def visit_call_(self, call_node: relax.Call) -> relax.Call:
call_tir_op = tvm.ir.Op.get("relax.call_tir")
if call_node.op != call_tir_op:
return call_node
var = call_node.args[0]
func = self.mod_[var]
if call_node.args[1][0].ty.dtype == "uint8":
if op_replace(call_node, func):
inp, inp_scale, inp_zp, out_scale, out_zp = [x for x in call_node.args[1]]
# LUT node creation
LUT = hexagon_unary_ops.LUT_generation(
inp_scale, inp_zp, out_scale, out_zp, call_node.args[0].name_hint
)
# Take operation node creation
take_func = hexagon_unary_ops.generate_take_primfunc(inp, call_node.ty)
take_func = take_func.without_attr("global_symbol")
take_func_gv = self.builder_.add_func(take_func, "take")
take_node = relax.call_tir(
take_func_gv,
relax.expr.Tuple(
[call_node.args[1][0], relax.expr.Constant(tvm.runtime.tensor(LUT))]
),
call_node.ty,
)
return take_node
return call_node
@tvm.ir.transform.module_pass(opt_level=2, name="replace_tanh_take")
class PassReplaceWithTakeOpPrimFuncs:
def transform_module(self, mod, ctx):
return Tanh2TakeReplace(mod).transform()