# 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: F841 # 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=invalid-name, unused-variable "Schedules for Texture Based Layout Transforms" from tvm import s_tir, tirx from tvm.target import Target from .. import analysis from .base import AdrenoScheduleRule class LayoutTransform(AdrenoScheduleRule): """Texture based Layout Transform Dlight Schedule for Adreno""" def __init__(self, use_op_name=True): self.use_op_name = use_op_name # TODO: Try using Coalesced Writes... def apply( # pylint: disable=too-many-locals self, func: tirx.PrimFunc | s_tir.Schedule, target: Target, _: bool, ) -> None | s_tir.Schedule | list[s_tir.Schedule]: # pylint: disable=invalid-name if not (isinstance(func, tirx.PrimFunc | s_tir.Schedule)) or not self.is_target_available( target ): return None if isinstance(func, tirx.PrimFunc): sch = s_tir.Schedule(func) sch.work_on("main") elif isinstance(func, s_tir.Schedule): sch = func root_block = analysis.get_root_block(sch, sch.func_working_on) if len(sch.get_child_blocks(root_block)) != 1: return None blk = sch.get_child_blocks(root_block)[0] block_info = analysis.get_sblock_info(sch, blk) if not ( (self.use_op_name and block_info.name == "te_layout_transform") or (not self.use_op_name and block_info.is_layout_transform(sch)) ): return None read_buf, write_buf = (block_info.read_bufs(sch)[0], block_info.write_bufs(sch)[0]) lps = block_info.get_loops() lpv_read, lpv_write = ( read_buf.assoc_lps[-1], write_buf.assoc_lps[-1], ) if lpv_read is None or lpv_write is None: return None vlen_read, vlen_write = read_buf.get_vecsize(), write_buf.get_vecsize() local_cache = sch.get(lpv_read) != sch.get(lpv_write) or vlen_read != vlen_write block_loops = [ lp for lp in lps if sch.get(lp) != sch.get(lpv_read) and sch.get(lp) != sch.get(lpv_write) ] vec_loops = ( [lpv_read, lpv_write] if sch.get(lpv_read) != sch.get(lpv_write) else (lpv_read,) ) sch.reorder(*block_loops, *vec_loops) if local_cache: if sch.get(lpv_read) != sch.get(lpv_write): blp_read, vlp_read = sch.split( lpv_read, [None, vlen_read], preserve_unit_iters=True ) blp_write, vlp_write = sch.split( lpv_write, [None, vlen_write], preserve_unit_iters=True ) sch.reorder(blp_read, blp_write, vlp_read, vlp_write) block_loops += [blp_read, blp_write] rblk = sch.cache_read(blk, 0, "local") sch.compute_at(rblk, block_loops[-1], preserve_unit_loops=True) sch.vectorize(sch.get_loops(rblk)[-1]) sch.vectorize(vlp_write) else: if vlen_read > vlen_write: read_lp, vec_lp = sch.split(blk, [None, vlen_write], preserve_unit_iters=True) rblk = sch.cache_read(blk, 0, "local") sch.compute_at(rblk, read_lp, preserve_unit_loops=True) sch.vectorize(sch.get_loops(rblk)[-1]) sch.vectorize(vec_lp) else: rblk = sch.cache_read(blk, 0, "local") sch.compute_at(rblk, block_loops[-1], preserve_unit_loops=True) _, vread_lp = sch.split( sch.get_loops(rblk)[-1], vlen_read, preserve_unit_iters=True ) sch.vectorize(vread_lp) sch.vectorize(vlp_write) else: blp, vlp = sch.split(lpv_read, [None, vlen_read], preserve_unit_iters=True) block_loops += [blp] sch.vectorize(vlp) b = sch.fuse(*block_loops) tx_extent = min(sch.get(b).extent, 256) candidates = [1, 2, 4, 8, 16, 32] bx, tx = sch.split(b, [None, 256], preserve_unit_iters=True) sch.bind(bx, "blockIdx.x") sch.bind(tx, "threadIdx.x") return sch