tvm
Loading...
Searching...
No Matches
broadcast.h
Go to the documentation of this file.
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
24#ifndef TVM_TOPI_DETAIL_BROADCAST_H_
25#define TVM_TOPI_DETAIL_BROADCAST_H_
26
27#include <tvm/te/operation.h>
29
30#include <algorithm>
31#include <deque>
32#include <string>
33
34namespace tvm {
35namespace topi {
36namespace detail {
37
38struct BroadcastHelper {
39 std::deque<tvm::PrimExpr> common_shape;
40 std::deque<tvm::tirx::PrimVar> all_vars;
41 std::deque<tvm::tirx::PrimVar> vars1;
42 std::deque<tvm::tirx::PrimVar> vars2;
43};
44
45static inline PrimType CommonType(const PrimType& type1, const PrimType& type2) {
46 TVM_FFI_ICHECK(type1.IsScalar() && type2.IsScalar());
47 TVM_FFI_ICHECK(type1.code() == type2.code());
48 return type1.bits() < type2.bits() ? type1.WithBits(type2.bits()) : type1;
49}
50
51inline BroadcastHelper BroadcastShape(const tvm::ffi::Array<tvm::PrimExpr>& shape1,
52 const tvm::ffi::Array<tvm::PrimExpr>& shape2) {
53 BroadcastHelper bh;
54 int s1_size = shape1.size();
55 int s2_size = shape2.size();
56 tvm::PrimExpr one(1);
57 int i;
58
59 auto cast_if_needed = [](PrimType to_type, PrimExpr expr) {
60 return to_type == expr.ty() ? expr : cast(to_type, expr);
61 };
62
63 for (i = 1; i <= std::min(s1_size, s2_size); ++i) {
64 // TODO(@icemelon9): Need to revisit this part
65 const IntImmNode* static_size1 = shape1[s1_size - i].as<IntImmNode>();
66 const IntImmNode* static_size2 = shape2[s2_size - i].as<IntImmNode>();
67 PrimType common_type = CommonType(shape1[s1_size - i].ty(), shape2[s2_size - i].ty());
68
69 bh.all_vars.push_front(tvm::tirx::PrimVar("dim", common_type));
70 if (topi::detail::EqualCheck(shape1[s1_size - i], shape2[s2_size - i])) {
71 bh.common_shape.push_front(cast_if_needed(common_type, shape1[s1_size - i]));
72 bh.vars1.push_front(bh.all_vars[0]);
73 bh.vars2.push_front(bh.all_vars[0]);
74 } else if (topi::detail::EqualCheck(one, shape1[s1_size - i])) {
75 TVM_FFI_ICHECK(!topi::detail::EqualCheck(one, shape2[s2_size - i]));
76 bh.common_shape.push_front(cast_if_needed(common_type, shape2[s2_size - i]));
77 bh.vars2.push_front(bh.all_vars[0]);
78 } else if (topi::detail::EqualCheck(one, shape2[s2_size - i])) {
79 bh.common_shape.push_front(cast_if_needed(common_type, shape1[s1_size - i]));
80 bh.vars1.push_front(bh.all_vars[0]);
81 } else if (!static_size1 && !static_size2) {
82 bh.common_shape.push_front(
83 cast_if_needed(common_type, max(shape1[s1_size - i], shape2[s2_size - i])));
84 bh.vars1.push_front(bh.all_vars[0]);
85 bh.vars2.push_front(bh.all_vars[0]);
86 } else if (!static_size1) {
87 bh.common_shape.push_front(cast_if_needed(common_type, shape2[s2_size - i]));
88 bh.vars2.push_front(bh.all_vars[0]);
89 bh.vars1.push_front(bh.all_vars[0]);
90 } else if (!static_size2) {
91 bh.common_shape.push_front(cast_if_needed(common_type, shape1[s1_size - i]));
92 bh.vars1.push_front(bh.all_vars[0]);
93 bh.vars2.push_front(bh.all_vars[0]);
94 } else {
95 TVM_FFI_ICHECK(false) << "Incompatible broadcast dims: " << shape1[s1_size - i] << " and "
96 << shape2[s2_size - i] << " in: "
97 << tvm::ffi::Array<tvm::PrimExpr>(shape1.begin(), shape1.end())
98 << " and "
99 << tvm::ffi::Array<tvm::PrimExpr>(shape2.begin(), shape2.end());
100 }
101 }
102 // Remaining dimensions whether on shape1 or shape2 can always be completed
103 auto max_size = std::max(s1_size, s2_size);
104 auto& shape = (s1_size > s2_size) ? shape1 : shape2;
105 auto& vars = (s1_size > s2_size) ? bh.vars1 : bh.vars2;
106 for (; i <= max_size; ++i) {
107 bh.all_vars.push_front(tvm::tirx::PrimVar("v", shape[max_size - 1].ty()));
108 bh.common_shape.push_front(shape[max_size - i]);
109 vars.push_front(bh.all_vars[0]);
110 }
111 return bh;
112}
113
114inline tvm::ffi::Array<tvm::PrimExpr> InputIndexFromBroadcast(
115 const tvm::ffi::Array<tvm::tirx::PrimVar>& ovars, const tvm::te::Tensor& T,
116 const std::deque<tvm::tirx::PrimVar>& my_vars, const std::deque<tvm::tirx::PrimVar>& all_vars) {
117 tvm::ffi::Array<tvm::PrimExpr> ivars;
118 TVM_FFI_ICHECK_EQ(ovars.size(), all_vars.size());
119 // N^2, could use a map but NBD.
120 size_t expected_dims = T->shape.size();
121 for (size_t i = 0; i < ovars.size(); ++i) {
122 bool found = false;
123 for (size_t j = 0; j < my_vars.size(); ++j) {
124 if (all_vars[i].same_as(my_vars[j])) {
125 ivars.push_back(ovars[i]);
126 found = true;
127 break;
128 }
129 }
130 // Only inject 0 here if we have not yet reached the dimension of I
131 // (i.e. this must be a 1)
132 if (!found && (ovars.size() - i) <= expected_dims) {
133 ivars.push_back(tvm::IntImm(ovars[i].ty(), 0));
134 }
135 }
136 TVM_FFI_ICHECK(expected_dims == ivars.size());
137 return ivars;
138}
139
140template <typename FBinaryExpr>
141inline tvm::te::Tensor WithBroadcast(FBinaryExpr op, const tvm::te::Tensor& A,
142 const tvm::te::Tensor& B, const std::string& name = "tensor",
143 const std::string& tag = "") {
144 auto bh = BroadcastShape(A->shape, B->shape);
145 auto l = [&](tvm::ffi::Array<tvm::tirx::PrimVar> ovars) {
146 return op(A(InputIndexFromBroadcast(ovars, A, bh.vars1, bh.all_vars)),
147 B(InputIndexFromBroadcast(ovars, B, bh.vars2, bh.all_vars)));
148 };
149 return tvm::te::compute(
150 tvm::ffi::Array<tvm::PrimExpr>(bh.common_shape.begin(), bh.common_shape.end()), l, name, tag);
151}
152
153} // namespace detail
154} // namespace topi
155} // namespace tvm
156
157#endif // TVM_TOPI_DETAIL_BROADCAST_H_
Managed reference class to IntImmNode.
Definition expr.h:504
Typed reference/view over any Expr whose ExprNode::ty is PrimType.
Definition base_expr.h:401
Tensor structure representing a possible input, or intermediate computation result.
Definition tensor.h:98
Checked scalar view over a VarNode.
Definition var.h:46
Utility functions for handling constants in TVM expressions.
tvm::IntImmNode IntImmNode
Definition expr.h:49
Tensor compute(ffi::Array< PrimExpr > shape, FCompute fcompute, std::string name="tensor", std::string tag="", ffi::Map< ffi::String, ffi::Any > attrs={})
Construct a new tensor by computing over shape, using the computation rule: result_tensor[axis] = fco...
const Op & max()
const Op & cast()
See pesudo code below:
Tensor shape(const Tensor &src, PrimType dtype, const std::string name="T_shape", const std::string tag=kInjective)
Get the shape of input tensor.
Definition transform.h:2009
An object that builds and maintains block scope and StmtSref mapping for Dependence analysis.
Definition analyzer.h:40
Operation node can generate one or multiple Tensors.