/** * Copyright 2019 Huawei Technologies Co., Ltd * * Licensed 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. */ #include "insn_info.h" #include #include #include #include #include #include #include #include #include #include #include "common/array_api.h" #include "common/util_cce.h" #include "src/common/util.h" #include "cce_params.h" #include "pass/expr_alg_simplify.h" namespace air { /// Check if two StmtStoreInfo are equal /// \param lhs - Left arg /// \param rhs - Right arg /// \return bool - Whether they are equal or not bool Equal(const akg::StmtStoreInfo &lhs, const akg::StmtStoreInfo &rhs) { return lhs == rhs; } } // namespace air namespace akg { TVM_REGISTER_NODE_TYPE(StmtStoreInfoNode); TVM_REGISTER_NODE_TYPE(VectorArgInfoNode); TVM_REGISTER_NODE_TYPE(ArgInfoNode); using air::runtime::PackedFunc; using air::runtime::TVMArgs; using air::runtime::TVMRetValue; IterVar CCE_AXIS_VAR = thread_axis(Range(), "cce"); /// GetCceAxis variable /// \return CCE_AXIS_VAR IterVar GetCceAxis() { return CCE_AXIS_VAR; } /// Move flex_var back to elem_offset void StmtStoreInfo::CleanFlexVar() { auto node = GetNode(); CHECK(node != nullptr); for (auto v : node->flex_var_) { size_t loc = 0; if (GetIndexOfElement(node->var_, v, loc)) { auto stride = node->strides_[loc]; node->var_ = RemoveItemAtIndex(node->var_, loc); node->strides_ = RemoveItemAtIndex(node->strides_, loc); node->shape_ = RemoveItemAtIndex(node->shape_, loc); node->elem_offset_ += v * stride; } } node->flex_var_ = Array(); } /// Make a StmtStoreInfo copy /// \return com_info - Copied StmtStoreInfo StmtStoreInfo StmtStoreInfo::Copy() const { auto com_info = StmtStoreInfo(make_node()); auto node = com_info.GetNode(); auto this_node = this->GetNode(); CHECK(this_node != nullptr); node->strides_ = this_node->strides_; node->shape_ = this_node->shape_; node->var_ = this_node->var_; node->flex_var_ = this_node->flex_var_; node->scope_ = this_node->scope_; node->name_ = this_node->name_; node->index_ = this_node->index_; node->elem_offset_ = this_node->elem_offset_; node->insn_offset_ = this_node->insn_offset_; node->dtype_ = this_node->dtype_; node->data_alignment_ = this_node->data_alignment_; node->data_ = this_node->data_; node->buffer_ = this_node->buffer_; return com_info; } /// Make a StmtInfo copy /// \return StmtInfo StmtInfo::Copy() const { auto stmt_info = StmtInfo(); stmt_info.ops_ = ops_; for (auto var : vars_) { auto new_var = Variable::make(var->type, var->name_hint); stmt_info.vars_.push_back(new_var); } for (size_t i = 0; i < vars_.size(); ++i) { for (size_t j = 0; j < stmt_info.ops_.size(); ++j) { auto new_op = substitute(vars_[i], stmt_info.vars_[i], stmt_info.ops_[j]); auto for_op = new_op.as(); CHECK(for_op != nullptr); new_op = For::make(stmt_info.vars_[j], for_op->min, for_op->extent, for_op->for_type, for_op->device_api, for_op->body); stmt_info.ops_.Set(j, new_op); } } return stmt_info; } /// Replace a / target * target /// \param value /// \param target /// \return int FloorTo(int value, int target) { CHECK_NE(target, 0); return value / target * target; } /// Replace (a + target - 1) / target * target /// \param value /// \param target /// \return int CeilTo(int value, int target) { CHECK_NE(target, 0); return (value + target - 1) / target * target; } /// Eliminate vars in expr /// \param e - Expr to be processed /// \param vars - Var list to be eliminated /// \return e - Processed expr Expr EliminateVarInExpr(Expr e, const Array &vars) { if (vars.empty()) { return e; } Map var_dict; for (auto var : vars) { var_dict.Set(var, Expr(0)); } e = Substitute(e, var_dict); e = Simplify(e); return e; } /// Sort three arrays with one rule /// \param vars - Var list to be Sorted /// \param shapes - Shape list to be Sorted /// \param strides - Stride list to be Sorted /// \param reverse - Reverse order or not void SortVarShapeAndStride(Array &vars, Array &shapes, Array &strides, bool reverse) { size_t size = std::min(vars.size(), std::min(shapes.size(), strides.size())); vars = GetRange(vars, 0, size); shapes = GetRange(shapes, 0, size); strides = GetRange(strides, 0, size); for (size_t i = 1; i < size; ++i) { for (size_t j = i; j > 0; --j) { CHECK(strides[j - 1].as()); auto l_value = strides[j - 1].as()->value; CHECK(strides[j].as()); auto r_value = strides[j].as()->value; bool cmp = reverse ? r_value > l_value : r_value < l_value; if (cmp) { auto stride = strides[j]; strides.Set(j, strides[j - 1]); strides.Set(j - 1, stride); auto var = vars[j]; vars.Set(j, vars[j - 1]); vars.Set(j - 1, var); auto shape = shapes[j]; shapes.Set(j, shapes[j - 1]); shapes.Set(j - 1, shape); } else if (r_value == l_value) { auto l_var = vars[j - 1].get(); auto r_var = vars[j].get(); if (l_var < r_var) { auto stride = strides[j]; strides.Set(j, strides[j - 1]); strides.Set(j - 1, stride); auto var = vars[j]; vars.Set(j, vars[j - 1]); vars.Set(j - 1, var); auto shape = shapes[j]; shapes.Set(j, shapes[j - 1]); shapes.Set(j - 1, shape); } } } } } /// Get buffer's scope from its name /// \param name - Name of buffer /// \return string - Scope string std::string GetBufScope(const std::string &name) { std::map mem_dict = {{"UB", SCOPE_UBUF}, {"L1", SCOPE_CBUF}, {"L0A", SCOPE_CA}, {"L0B", SCOPE_CB}, {"L0C", SCOPE_CC}, {"REG", SCOPE_REG}}; std::vector split_list = air::common::Split(name, '.'); if (split_list.size() == 1) { split_list = akg::common::Split(name, "_local_"); } std::string key = split_list[split_list.size() - 1]; for (auto &iter : mem_dict) { std::string::size_type pos = split_list[split_list.size() - 1].find(iter.first); if (pos != std::string::npos) { key = iter.first; break; } } if (split_list.size() == 1) { return DMA_COPY_GLOBAL; } return mem_dict[key]; } /// Get insn offset by eliminate vars /// \param com_info - Computation info /// \param elim_var - Vars to be eliminated /// \return expr - Processed expr Expr GetInsnOffset(const StmtStoreInfo &com_info, const Array &elim_var) { auto inner_index = Simplify(Sub::make(com_info->index_, com_info->elem_offset_)); auto insn_offset = EliminateVarInExpr(inner_index, elim_var); return insn_offset; } /// Get for_info from stmt /// \param s - Input stmt /// \return for_info - For loop Information StmtInfo GetForInfo(const Stmt &s) { StmtInfo if_info; StmtInfo for_info; GetIfForInfo(s, if_info, for_info); return for_info; } /// Get if stmt and for stmt from a stmt /// \param s /// \param if_info /// \param for_info void GetIfForInfo(const Stmt &s, StmtInfo &if_info, StmtInfo &for_info) { if (s->IsInstance()) { const auto attrstmt_ptr = s.as(); GetIfForInfo(attrstmt_ptr->body, if_info, for_info); } if (s->IsInstance()) { const auto for_ptr = s.as(); for_info.vars_.push_back(for_ptr->loop_var); for_info.ops_.push_back(s); GetIfForInfo(for_ptr->body, if_info, for_info); } if (s->IsInstance()) { if_info.ops_.push_back(s); const auto ifthenelse_ptr = s.as(); const Expr &condition_ = ifthenelse_ptr->condition; auto temp_vars = GetVarsInExpr(condition_); for (auto iter : temp_vars) { if_info.vars_.push_back(iter); } if (ifthenelse_ptr->else_case.defined()) { LOG(FATAL) << "Unsupport \'else\' condition yet."; } GetIfForInfo(ifthenelse_ptr->then_case, if_info, for_info); } } /// Get the children of expr of binary operation /// \param e - Expr to be processed /// \return Array e.a and e.b - If e is not binary op, then return empty Array. Array GetBinaryOpExprChildren(const Expr &e) { Array children; if (auto add = e.as()) { children.push_back(add->a); children.push_back(add->b); return children; } else if (auto sub = e.as()) { children.push_back(sub->a); children.push_back(sub->b); return children; } else if (auto mul = e.as()) { children.push_back(mul->a); children.push_back(mul->b); return children; } else if (auto div = e.as
()) { children.push_back(div->a); children.push_back(div->b); return children; } else if (auto f_div = e.as()) { children.push_back(f_div->a); children.push_back(f_div->b); return children; } else if (auto mod = e.as()) { children.push_back(mod->a); children.push_back(mod->b); return children; } else if (auto f_mod = e.as()) { children.push_back(f_mod->a); children.push_back(f_mod->b); return children; } else if (auto min = e.as()) { children.push_back(min->a); children.push_back(min->b); return children; } else if (auto max = e.as()) { children.push_back(max->a); children.push_back(max->b); return children; } else if (auto eq = e.as()) { children.push_back(eq->a); children.push_back(eq->b); return children; } else if (auto ne = e.as()) { children.push_back(ne->a); children.push_back(ne->b); return children; } else if (auto lt = e.as()) { children.push_back(lt->a); children.push_back(lt->b); return children; } else if (auto le = e.as()) { children.push_back(le->a); children.push_back(le->b); return children; } else if (auto gt = e.as()) { children.push_back(gt->a); children.push_back(gt->b); return children; } else if (auto ge = e.as()) { children.push_back(ge->a); children.push_back(ge->b); return children; } else if (auto and_op = e.as()) { children.push_back(and_op->a); children.push_back(and_op->b); return children; } else if (auto or_op = e.as()) { children.push_back(or_op->a); children.push_back(or_op->b); return children; } else { return children; } } /// Get all store nodes in stmt /// \param s - Input stmt /// \return Array - All stores in stmt Array GetStores(const Stmt &s) { Array stores; Array loads; GetStoreAndLoads(s, stores, loads); Array result; std::transform(stores.begin(), stores.end(), std::back_inserter(result.CopyOnWrite()->data), [](const NodeRef &v) { return (Downcast(v)); }); return result; } /// Get stores and loads from a stmt /// \param s - Stmt to be processed /// \param stores - Store list to be returned /// \param loads - Load list to be returned void GetStoreAndLoads(const Stmt &s, Array &stores, Array &loads) { // Get stores Array enable; enable.push_back(Expr("Store")); PackedFunc post_order = PackedFunc([&stores](const TVMArgs args, TVMRetValue *ret) { const auto &sptr = args[0].operator ObjectRef(); if (sptr->IsInstance()) { stores.push_back(args[0]); } *ret = TVMRetValue(); }); static_cast(air::ir::IRTransform(s, PackedFunc{nullptr}, post_order, enable)); // Get loads in store PackedFunc pre_order; pre_order = PackedFunc([&loads, &pre_order](const TVMArgs args, TVMRetValue *ret) { Expr val = args[0]; if (val.as()) { loads.push_back(val); } else if (val->IsInstance()) { auto tmp_args = val.as()->args; for (auto tmp_arg : tmp_args) { static_cast(pre_order(tmp_arg)); } } else if (val->IsInstance(); static_cast(pre_order(tmp_args->condition)); static_cast(pre_order(tmp_args->true_value)); static_cast(pre_order(tmp_args->false_value)); } else if (val->IsInstance()) { auto tmp_val = val.as()->value; static_cast(pre_order(tmp_val)); } else { Array tmp = GetBinaryOpExprChildren(val); if (!tmp.empty()) { static_cast(pre_order(tmp[0])); static_cast(pre_order(tmp[1])); } } *ret = TVMRetValue(); }); for (auto store : stores) { if (store->IsInstance()) { auto val = store.as()->value; static_cast(pre_order(val)); } } } /// Get buffer data alignment, depending on scope and data type also called 'block_size' in buffer /// \param dst_info - Computation info of dst /// \param src_info - Computation info of src /// \return int - Scope block size int GetScopeBlockSize(const StmtStoreInfo &dst_info, const StmtStoreInfo &src_info) { auto dtype = dst_info->dtype_; auto data_width = dtype.bits(); auto dst_scope = dst_info->scope_; auto src_scope = src_info->scope_; auto data_bytes = GLB_ELEM_BYTES; // Load 2D if (dst_scope == SCOPE_CB || dst_scope == SCOPE_CA || src_scope == SCOPE_CB || src_scope == SCOPE_CA) { data_bytes = BLOCK_IN * BLOCK_OUT * data_width / 8; if (data_width == 8) { data_bytes *= 2; } } else if (dst_scope == SCOPE_CC || src_scope == SCOPE_CC) { data_bytes = BLOCK_IN * BLOCK_OUT * data_width / 8; } CHECK_NE(data_width, 0); return data_bytes * 8 / data_width; } /// Get ub block size /// \param type - Type of store /// \return int - Block size int GetUbBlkSize(const Type &type) { CHECK_NE(type.bits(), 0); int result = GLB_ELEM_BYTES * 8 / type.bits(); CHECK_NE(result, 0) << "Get zero UB Block Size"; return result; } /// Get all Var in expr /// \param expr - Expr to be processed /// \return Array - List of var in expr Array GetVarsInExpr(const Expr &expr, bool exclude_upper_case_vars) { class VariableMutator : public IRMutator { public: explicit VariableMutator(Array &ivar_set, bool exclude_upper = false) : ivar_set_(ivar_set), exclude_upper_(exclude_upper) {} ~VariableMutator() override = default; Expr Mutate_(const Variable *op, const Expr &e) final { bool find_var = true; if (exclude_upper_) { for (auto c : op->name_hint) { if (c >= 'A' && c <= 'Z') { find_var = false; break; } } } if (find_var) { bool find = false; for (auto iter = ivar_set_.begin(); iter != ivar_set_.end(); ++iter) { if ((*iter).get() == op) { find = true; break; } } if (!find) { ivar_set_.push_back(Downcast(e)); } } return e; } Array &ivar_set_; bool exclude_upper_{false}; }; Array ivar_set; VariableMutator(ivar_set, exclude_upper_case_vars).Mutate(expr); return ivar_set; } /// Get all variables in the given expr /// \param expr - Input expr /// \return unordered_set of variables std::unordered_set GetVariablesInExpr(const Expr &expr) { std::unordered_set vars; PostOrderVisit(expr, [&vars](const NodeRef &node) { if (const auto v = node.as()) { vars.insert(v); } }); return vars; } /// Clean non-linear loop vars in index and offset /// \param dst_info_list - Dst info list to be cleaned /// \param src_info_list - Src info list to be cleaned /// \param if_info - If info of above infos void CleanNonLinearVar(const StmtInfoList &dst_info_list, const StmtInfoList &src_info_list, const StmtInfo &if_info) { Map offset_vars; for (auto dst_info : dst_info_list) { auto vars = GetVarsInExpr(dst_info->elem_offset_); for (auto e : vars) { offset_vars.Set(e, Expr(0)); } } for (auto src_info : src_info_list) { auto vars = GetVarsInExpr(src_info->elem_offset_); for (auto e : vars) { offset_vars.Set(e, Expr(0)); } } for (auto dst_info : dst_info_list) { auto dst_var = dst_info->var_; auto dst_shape = dst_info->shape_; auto dst_strides = dst_info->strides_; auto dst_index = dst_info->index_; if (src_info_list.empty()) { // clean dst_vars for (uint64_t i = 0; i < dst_info->var_.size(); ++i) { auto d_var = dst_info->var_[i]; auto is_flex_var = IsFlexVarInIf(d_var, if_info.ops_); if (IsInArray(if_info.vars_, VarExpr(d_var))) { if (i == dst_info->var_.size() - 1 && is_flex_var) { dst_info.GetNode()->flex_var_.push_back(d_var); } else { size_t index = 0; if (GetIndexOfElement(dst_var, d_var, index)) { dst_var = RemoveItemAtIndex(dst_var, index); dst_shape = RemoveItemAtIndex(dst_shape, index); dst_strides = RemoveItemAtIndex(dst_strides, index); } } } } dst_info.GetNode()->var_ = dst_var; } else { for (auto src_info : src_info_list) { auto src_var = src_info->var_; auto src_shape = src_info->shape_; auto src_strides = src_info->strides_; auto src_index = src_info->index_; // clean dst_vars for (uint64_t i = 0; i < dst_info->var_.size(); ++i) { auto d_var = dst_info->var_[i]; auto is_flex_var = IsFlexVarInIf(d_var, if_info.ops_); if (i == dst_info->var_.size() - 1 && IsInArray(if_info.vars_, VarExpr(d_var)) && offset_vars.count(d_var) == 0 && is_flex_var) { dst_info.GetNode()->flex_var_.push_back(d_var); } else if (offset_vars.count(d_var) != 0 || IsInArray(if_info.vars_, VarExpr(d_var))) { size_t index = 0; if (GetIndexOfElement(dst_var, d_var, index)) { dst_var = RemoveItemAtIndex(dst_var, index); dst_shape = RemoveItemAtIndex(dst_shape, index); dst_strides = RemoveItemAtIndex(dst_strides, index); } } } dst_info.GetNode()->var_ = dst_var; // clean src_vars for (uint64_t i = 0; i < src_info->var_.size(); ++i) { auto s_var = src_info->var_[i]; auto is_flex_var = IsFlexVarInIf(s_var, if_info.ops_); if (i == src_info->var_.size() - 1 && IsInArray(if_info.vars_, VarExpr(s_var)) && offset_vars.count(s_var) == 0 && is_flex_var) { src_info.GetNode()->flex_var_.push_back(s_var); } else if (offset_vars.count(s_var) != 0 || IsInArray(if_info.vars_, VarExpr(s_var))) { size_t index = 0; if (GetIndexOfElement(src_var, s_var, index)) { src_var = RemoveItemAtIndex(src_var, index); src_shape = RemoveItemAtIndex(src_shape, index); src_strides = RemoveItemAtIndex(src_strides, index); } } } src_info.GetNode()->var_ = src_var; if (src_var.empty()) { src_info.GetNode()->shape_ = {Expr(1)}; src_info.GetNode()->strides_ = {Expr(1)}; } else { src_info.GetNode()->shape_ = src_shape; src_info.GetNode()->strides_ = src_strides; } src_info.GetNode()->elem_offset_ = EliminateVarInExpr(src_index, src_var); } } if (dst_var.empty()) { dst_info.GetNode()->shape_ = {Expr(1)}; dst_info.GetNode()->strides_ = {Expr(1)}; } else { dst_info.GetNode()->shape_ = dst_shape; dst_info.GetNode()->strides_ = dst_strides; } dst_info.GetNode()->elem_offset_ = EliminateVarInExpr(dst_index, dst_var); } } /// Substitute the Div call of 'DivRoundToZero' to safe div method /// The DivSubstitutor is a method of IRMutator namespace { class DivSubstitutor : public IRMutator { public: Expr Mutate_(const Call *op, const Expr &e) final { auto expr = IRMutator::Mutate_(op, e); const auto call = expr.as(); CHECK(call != nullptr); if (call->name == "DivRoundToZero") { if (call->args.size() != 2) { LOG(FATAL) << "Error: must have exactly two parameters here!"; } if (GetIntConst(call->args[1]) <= 0) { LOG(WARNING) << "Warning: the divisor is not constant or it is less than 0!"; } auto cond = LT::make(call->args[0], IntImm::make(Int(32), 0)); auto t_value = Div::make(Sub::make(Add::make(call->args[0], call->args[1]), IntImm::make(Int(32), 1)), call->args[1]); auto f_value = Div::make(call->args[0], call->args[1]); return Simplify(Select::make(cond, t_value, f_value)); } return expr; } }; } // namespace /// Get computation info from a stmt /// \param stores - Array of Store/Load /// \param for_info /// \return StmtInfoList - Array of computation StmtInfoList GetComputationInfo(const Array &stores, const StmtInfo &for_info) { Array for_extents; for (auto op : for_info.ops_) { CHECK(op.as()); for_extents.push_back(op.as()->extent); } StmtInfoList com_info_list; auto CleanNonLinearVarsInIndex = [](Array &var_list, Array &shape_list, const Expr &index) { // to solve the DetectLinearEquation problem // for index = (((((cc0/2) + cc6)*16) + cc7) - ((((cc6*2) + cc0)/4)*32)) // if cur_vars = [cc6, cc7], the strides will returned as [] // expect is cleaned cur_vars = [cc7], and returned strides = [1] auto tmp_var_list = var_list; for (auto var : tmp_var_list) { auto tmp_strides = air::arith::DetectLinearEquation(index, {var}); if (tmp_strides.empty() || air::arith::Analyzer().CanProve(tmp_strides[0] <= 0)) { size_t idx = 0; if (GetIndexOfElement(var_list, var, idx)) { var_list = RemoveItemAtIndex(var_list, idx); shape_list = RemoveItemAtIndex(shape_list, idx); } } } }; for (auto s : stores) { auto info = StmtStoreInfo(make_node()); const Store *store = nullptr; const Load *load = nullptr; Expr index; Expr predicate; if (s->IsInstance()) { store = s.as(); index = store->index; predicate = store->predicate; } else if (s->IsInstance()) { load = s.as(); index = load->index; predicate = load->predicate; } // Substitute the 'DivRoundToZero' in index expression to avoid // problems during CCE generation. index = DivSubstitutor().Mutate(index); // get vars in store auto index_vars = GetVarsInExpr(index); // get current vars of index_vars and for_vars Array cur_vars = IntersectionArray(index_vars, for_info.vars_); Array cur_shapes; for (size_t i = 0; i < cur_vars.size(); ++i) { size_t idx = 0; bool suc = GetIndexOfElement(for_info.vars_, cur_vars[i], idx); CHECK(suc); cur_shapes.push_back(for_extents[idx]); } if (cur_vars.empty() || for_info.vars_.empty()) { // store is a variable, not an array info.GetNode()->shape_.push_back(Expr(1)); info.GetNode()->strides_.push_back(Expr(1)); info.GetNode()->elem_offset_ = Expr(0); } else { // store is an array // order cur_vars // get stride of store index Array strides = air::arith::DetectLinearEquation(index, cur_vars); // if cur_vars have non-linear vars, then clean the vars if (strides.empty()) { CleanNonLinearVarsInIndex(cur_vars, cur_shapes, index); strides = air::arith::DetectLinearEquation(index, cur_vars); } // strides with complicate expr, then strides will be [], so the vars and shapes should be [] too if (strides.empty()) { cur_vars = {}; cur_shapes = {}; } bool is_all_const = false; int const_cnt = 0; for (auto &e : strides) { if (IsConstExpr(e)) { const_cnt++; } } for (auto &e : cur_shapes) { if (IsConstExpr(e)) { const_cnt++; } } if (strides.size() > 0 && const_cnt == static_cast(strides.size() + cur_shapes.size())) { is_all_const = true; } // only keep positive strides and relate var and shape size_t var_len = cur_vars.size(); size_t var_idx = 0; while (var_idx < var_len) { bool temp_condition = false; if (is_all_const) { temp_condition = !IsConstExpr(cur_shapes[var_idx]) || !IsConstExpr(strides[var_idx]) || GetIntConst(strides[var_idx]) < 0; } else { temp_condition = IsConstExpr(strides[var_idx]) && GetIntConst(strides[var_idx]) < 0; } if (temp_condition) { cur_vars = RemoveItemAtIndex(cur_vars, var_idx); cur_shapes = RemoveItemAtIndex(cur_shapes, var_idx); strides = RemoveItemAtIndex(strides, var_idx); var_len -= 1; } else { var_idx += 1; } } // order vars with strides order if (!is_all_const) { strides = RemoveItemAtIndex(strides, -1); } else { SortVarShapeAndStride(cur_vars, cur_shapes, strides, true); } // get ordered current vars if (var_len == 0) { info.GetNode()->shape_.push_back(Expr(1)); info.GetNode()->strides_.push_back(Expr(1)); } else { info.GetNode()->var_ = cur_vars; info.GetNode()->shape_ = cur_shapes; info.GetNode()->strides_ = strides; } info.GetNode()->elem_offset_ = EliminateVarInExpr(index, cur_vars); } info.GetNode()->index_ = index; if (is_const(predicate)) { info.GetNode()->data_alignment_ = GetInt32Const(predicate) == FREE_ALIGN ? 0 : GetInt32Const(predicate); } else { info.GetNode()->data_alignment_ = FREE_ALIGN; } if (store != nullptr) { info.GetNode()->dtype_ = store->value.type(); info.GetNode()->scope_ = GetBufScope(store->buffer_var->name_hint); info.GetNode()->name_ = store->buffer_var->name_hint; info.GetNode()->data_ = Var(store->buffer_var); } else if (load != nullptr) { info.GetNode()->dtype_ = load->type; info.GetNode()->scope_ = GetBufScope(load->buffer_var->name_hint); info.GetNode()->name_ = load->buffer_var->name_hint; info.GetNode()->data_ = Var(load->buffer_var); } com_info_list.push_back(info); } return com_info_list; } /// Determining whether the stmt contains a select /// \param stmt - Input stmt /// \return has_select - True if the stmt contains a select, otherwise false bool HasSelect(const Stmt &stmt) { bool has_select = false; PostOrderVisit(stmt, [&has_select](const NodeRef &node) { if (node.as