annotate mlir/lib/Dialect/Traits.cpp @ 190:b1364f705114

relax tail call error on goto from normal function. args.c worked.
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Sat, 06 Jun 2020 13:15:35 +0900
parents 0572611fdcc8
children 2e18cbf3894f
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
150
anatofuz
parents:
diff changeset
1 //===- Traits.cpp - Common op traits shared by dialects -------------------===//
anatofuz
parents:
diff changeset
2 //
anatofuz
parents:
diff changeset
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
anatofuz
parents:
diff changeset
4 // See https://llvm.org/LICENSE.txt for license information.
anatofuz
parents:
diff changeset
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
anatofuz
parents:
diff changeset
6 //
anatofuz
parents:
diff changeset
7 //===----------------------------------------------------------------------===//
anatofuz
parents:
diff changeset
8
anatofuz
parents:
diff changeset
9 #include "mlir/Dialect/Traits.h"
anatofuz
parents:
diff changeset
10 #include "mlir/IR/StandardTypes.h"
anatofuz
parents:
diff changeset
11 #include "mlir/IR/TypeUtilities.h"
anatofuz
parents:
diff changeset
12 #include "llvm/Support/FormatVariadic.h"
anatofuz
parents:
diff changeset
13
anatofuz
parents:
diff changeset
14 using namespace mlir;
anatofuz
parents:
diff changeset
15
anatofuz
parents:
diff changeset
16 bool OpTrait::util::getBroadcastedShape(ArrayRef<int64_t> shape1,
anatofuz
parents:
diff changeset
17 ArrayRef<int64_t> shape2,
anatofuz
parents:
diff changeset
18 SmallVectorImpl<int64_t> &resultShape) {
anatofuz
parents:
diff changeset
19 // To compute the result broadcasted shape, we compare operand shapes
anatofuz
parents:
diff changeset
20 // element-wise: starting with the trailing dimensions, and working the
anatofuz
parents:
diff changeset
21 // way backward. Two dimensions are compatible when
anatofuz
parents:
diff changeset
22 // 1. they are equal, or
anatofuz
parents:
diff changeset
23 // 2. one of them is 1
anatofuz
parents:
diff changeset
24 // The result shape has the maximum among the two inputs at every
anatofuz
parents:
diff changeset
25 // dimension index.
anatofuz
parents:
diff changeset
26
anatofuz
parents:
diff changeset
27 resultShape.clear();
anatofuz
parents:
diff changeset
28 if (shape1.size() > shape2.size()) {
anatofuz
parents:
diff changeset
29 std::copy(shape1.begin(), shape1.end(), std::back_inserter(resultShape));
anatofuz
parents:
diff changeset
30 } else {
anatofuz
parents:
diff changeset
31 std::copy(shape2.begin(), shape2.end(), std::back_inserter(resultShape));
anatofuz
parents:
diff changeset
32 }
anatofuz
parents:
diff changeset
33
anatofuz
parents:
diff changeset
34 auto i1 = shape1.rbegin(), e1 = shape1.rend();
anatofuz
parents:
diff changeset
35 auto i2 = shape2.rbegin(), e2 = shape2.rend();
anatofuz
parents:
diff changeset
36 auto iR = resultShape.rbegin();
anatofuz
parents:
diff changeset
37
anatofuz
parents:
diff changeset
38 // Check each dimension is consistent.
anatofuz
parents:
diff changeset
39 for (; i1 != e1 && i2 != e2; ++i1, ++i2, ++iR) {
anatofuz
parents:
diff changeset
40 if (*i1 == -1 || *i2 == -1) {
anatofuz
parents:
diff changeset
41 // One or both dimensions is unknown. Follow TensorFlow behavior:
anatofuz
parents:
diff changeset
42 // - If either dimension is greater than 1, we assume that the program is
anatofuz
parents:
diff changeset
43 // correct, and the other dimension will be broadcast to match it.
anatofuz
parents:
diff changeset
44 // - If either dimension is 1, the other dimension is the output.
anatofuz
parents:
diff changeset
45 if (*i1 > 1) {
anatofuz
parents:
diff changeset
46 *iR = *i1;
anatofuz
parents:
diff changeset
47 } else if (*i2 > 1) {
anatofuz
parents:
diff changeset
48 *iR = *i2;
anatofuz
parents:
diff changeset
49 } else if (*i1 == 1) {
anatofuz
parents:
diff changeset
50 *iR = *i2;
anatofuz
parents:
diff changeset
51 } else if (*i2 == 1) {
anatofuz
parents:
diff changeset
52 *iR = *i1;
anatofuz
parents:
diff changeset
53 } else {
anatofuz
parents:
diff changeset
54 *iR = -1;
anatofuz
parents:
diff changeset
55 }
anatofuz
parents:
diff changeset
56 } else {
anatofuz
parents:
diff changeset
57 if (*i1 == *i2 || *i2 == 1) {
anatofuz
parents:
diff changeset
58 *iR = *i1;
anatofuz
parents:
diff changeset
59 } else if (*i1 == 1) {
anatofuz
parents:
diff changeset
60 *iR = *i2;
anatofuz
parents:
diff changeset
61 } else {
anatofuz
parents:
diff changeset
62 // This dimension of the two operand types is incompatible.
anatofuz
parents:
diff changeset
63 resultShape.clear();
anatofuz
parents:
diff changeset
64 return false;
anatofuz
parents:
diff changeset
65 }
anatofuz
parents:
diff changeset
66 }
anatofuz
parents:
diff changeset
67 }
anatofuz
parents:
diff changeset
68
anatofuz
parents:
diff changeset
69 return true;
anatofuz
parents:
diff changeset
70 }
anatofuz
parents:
diff changeset
71
anatofuz
parents:
diff changeset
72 /// Returns the shape of the given type. Scalars will be considered as having a
anatofuz
parents:
diff changeset
73 /// shape with zero dimensions.
anatofuz
parents:
diff changeset
74 static ArrayRef<int64_t> getShape(Type type) {
anatofuz
parents:
diff changeset
75 if (auto sType = type.dyn_cast<ShapedType>())
anatofuz
parents:
diff changeset
76 return sType.getShape();
anatofuz
parents:
diff changeset
77 return {};
anatofuz
parents:
diff changeset
78 }
anatofuz
parents:
diff changeset
79
anatofuz
parents:
diff changeset
80 /// Returns the result broadcast composition type from the two given types by
anatofuz
parents:
diff changeset
81 /// following NumPy broadcast semantics. Returned type may have dynamic shape if
anatofuz
parents:
diff changeset
82 /// either of the input types has dynamic shape. Returns null type if the two
anatofuz
parents:
diff changeset
83 /// given types are not broadcast-compatible.
anatofuz
parents:
diff changeset
84 ///
anatofuz
parents:
diff changeset
85 /// elementType, if specified, will be used as the element type of the
anatofuz
parents:
diff changeset
86 /// broadcasted result type. Otherwise it is required that the element type of
anatofuz
parents:
diff changeset
87 /// type1 and type2 is the same and this element type will be used as the
anatofuz
parents:
diff changeset
88 /// resultant element type.
anatofuz
parents:
diff changeset
89 Type OpTrait::util::getBroadcastedType(Type type1, Type type2,
anatofuz
parents:
diff changeset
90 Type elementType) {
anatofuz
parents:
diff changeset
91 // If the elementType is not specified, then the use the common element type
anatofuz
parents:
diff changeset
92 // of the inputs or fail if there is no common element type.
anatofuz
parents:
diff changeset
93 if (!elementType) {
anatofuz
parents:
diff changeset
94 elementType = getElementTypeOrSelf(type1);
anatofuz
parents:
diff changeset
95 if (elementType != getElementTypeOrSelf(type2))
anatofuz
parents:
diff changeset
96 return {};
anatofuz
parents:
diff changeset
97 }
anatofuz
parents:
diff changeset
98
anatofuz
parents:
diff changeset
99 // If one of the types is unranked tensor, then the other type shouldn't be
anatofuz
parents:
diff changeset
100 // vector and the result should have unranked tensor type.
anatofuz
parents:
diff changeset
101 if (type1.isa<UnrankedTensorType>() || type2.isa<UnrankedTensorType>()) {
anatofuz
parents:
diff changeset
102 if (type1.isa<VectorType>() || type2.isa<VectorType>())
anatofuz
parents:
diff changeset
103 return {};
anatofuz
parents:
diff changeset
104 return UnrankedTensorType::get(elementType);
anatofuz
parents:
diff changeset
105 }
anatofuz
parents:
diff changeset
106
anatofuz
parents:
diff changeset
107 // Returns the type kind if the given type is a vector or ranked tensor type.
anatofuz
parents:
diff changeset
108 // Returns llvm::None otherwise.
anatofuz
parents:
diff changeset
109 auto getCompositeTypeKind = [](Type type) -> Optional<StandardTypes::Kind> {
anatofuz
parents:
diff changeset
110 if (type.isa<VectorType>() || type.isa<RankedTensorType>())
anatofuz
parents:
diff changeset
111 return static_cast<StandardTypes::Kind>(type.getKind());
anatofuz
parents:
diff changeset
112 return llvm::None;
anatofuz
parents:
diff changeset
113 };
anatofuz
parents:
diff changeset
114
anatofuz
parents:
diff changeset
115 // Make sure the composite type, if has, is consistent.
anatofuz
parents:
diff changeset
116 auto compositeKind1 = getCompositeTypeKind(type1);
anatofuz
parents:
diff changeset
117 auto compositeKind2 = getCompositeTypeKind(type2);
anatofuz
parents:
diff changeset
118 Optional<StandardTypes::Kind> resultCompositeKind;
anatofuz
parents:
diff changeset
119
anatofuz
parents:
diff changeset
120 if (compositeKind1 && compositeKind2) {
anatofuz
parents:
diff changeset
121 // Disallow mixing vector and tensor.
anatofuz
parents:
diff changeset
122 if (compositeKind1 != compositeKind2)
anatofuz
parents:
diff changeset
123 return {};
anatofuz
parents:
diff changeset
124 resultCompositeKind = compositeKind1;
anatofuz
parents:
diff changeset
125 } else if (compositeKind1) {
anatofuz
parents:
diff changeset
126 resultCompositeKind = compositeKind1;
anatofuz
parents:
diff changeset
127 } else if (compositeKind2) {
anatofuz
parents:
diff changeset
128 resultCompositeKind = compositeKind2;
anatofuz
parents:
diff changeset
129 }
anatofuz
parents:
diff changeset
130
anatofuz
parents:
diff changeset
131 // Get the shape of each type.
anatofuz
parents:
diff changeset
132 SmallVector<int64_t, 4> resultShape;
anatofuz
parents:
diff changeset
133 if (!getBroadcastedShape(getShape(type1), getShape(type2), resultShape))
anatofuz
parents:
diff changeset
134 return {};
anatofuz
parents:
diff changeset
135
anatofuz
parents:
diff changeset
136 // Compose the final broadcasted type
anatofuz
parents:
diff changeset
137 if (resultCompositeKind == StandardTypes::Vector)
anatofuz
parents:
diff changeset
138 return VectorType::get(resultShape, elementType);
anatofuz
parents:
diff changeset
139 if (resultCompositeKind == StandardTypes::RankedTensor)
anatofuz
parents:
diff changeset
140 return RankedTensorType::get(resultShape, elementType);
anatofuz
parents:
diff changeset
141 return elementType;
anatofuz
parents:
diff changeset
142 }
anatofuz
parents:
diff changeset
143
anatofuz
parents:
diff changeset
144 /// Returns a tuple corresponding to whether range has tensor or vector type.
anatofuz
parents:
diff changeset
145 template <typename iterator_range>
anatofuz
parents:
diff changeset
146 static std::tuple<bool, bool> hasTensorOrVectorType(iterator_range types) {
anatofuz
parents:
diff changeset
147 return std::make_tuple(
anatofuz
parents:
diff changeset
148 llvm::any_of(types, [](Type t) { return t.isa<TensorType>(); }),
anatofuz
parents:
diff changeset
149 llvm::any_of(types, [](Type t) { return t.isa<VectorType>(); }));
anatofuz
parents:
diff changeset
150 }
anatofuz
parents:
diff changeset
151
anatofuz
parents:
diff changeset
152 static bool areCompatibleShapes(ArrayRef<int64_t> shape1,
anatofuz
parents:
diff changeset
153 ArrayRef<int64_t> shape2) {
anatofuz
parents:
diff changeset
154 auto isCompatible = [](int64_t dim1, int64_t dim2) {
anatofuz
parents:
diff changeset
155 return dim1 == dim2 || dim1 == -1 || dim2 == -1;
anatofuz
parents:
diff changeset
156 };
anatofuz
parents:
diff changeset
157 if (shape1.size() != shape2.size())
anatofuz
parents:
diff changeset
158 return false;
anatofuz
parents:
diff changeset
159 for (auto p : llvm::zip(shape1, shape2))
anatofuz
parents:
diff changeset
160 if (!isCompatible(std::get<0>(p), std::get<1>(p)))
anatofuz
parents:
diff changeset
161 return false;
anatofuz
parents:
diff changeset
162 return true;
anatofuz
parents:
diff changeset
163 }
anatofuz
parents:
diff changeset
164
anatofuz
parents:
diff changeset
165 static std::string getShapeString(ArrayRef<int64_t> shape) {
anatofuz
parents:
diff changeset
166 // TODO: should replace with printing shape more uniformly across here and
anatofuz
parents:
diff changeset
167 // when in type.
anatofuz
parents:
diff changeset
168 return std::string(
anatofuz
parents:
diff changeset
169 formatv("'{0:$[x]}'", llvm::make_range(shape.begin(), shape.end())));
anatofuz
parents:
diff changeset
170 }
anatofuz
parents:
diff changeset
171
anatofuz
parents:
diff changeset
172 LogicalResult OpTrait::impl::verifyCompatibleOperandBroadcast(Operation *op) {
anatofuz
parents:
diff changeset
173 // Ensure broadcasting only tensor or only vector types.
anatofuz
parents:
diff changeset
174 auto operandsHasTensorVectorType =
anatofuz
parents:
diff changeset
175 hasTensorOrVectorType(op->getOperandTypes());
anatofuz
parents:
diff changeset
176 auto resultsHasTensorVectorType = hasTensorOrVectorType(op->getResultTypes());
anatofuz
parents:
diff changeset
177 if ((std::get<0>(operandsHasTensorVectorType) ||
anatofuz
parents:
diff changeset
178 std::get<0>(resultsHasTensorVectorType)) &&
anatofuz
parents:
diff changeset
179 (std::get<1>(operandsHasTensorVectorType) ||
anatofuz
parents:
diff changeset
180 std::get<1>(resultsHasTensorVectorType)))
anatofuz
parents:
diff changeset
181 return op->emitError("cannot broadcast vector with tensor");
anatofuz
parents:
diff changeset
182
anatofuz
parents:
diff changeset
183 auto rankedOperands = make_filter_range(
anatofuz
parents:
diff changeset
184 op->getOperandTypes(), [](Type t) { return t.isa<RankedTensorType>(); });
anatofuz
parents:
diff changeset
185
anatofuz
parents:
diff changeset
186 // If all operands are unranked, then all result shapes are possible.
anatofuz
parents:
diff changeset
187 if (rankedOperands.empty())
anatofuz
parents:
diff changeset
188 return success();
anatofuz
parents:
diff changeset
189
anatofuz
parents:
diff changeset
190 // Compute broadcasted shape of operands (which requires that operands are
anatofuz
parents:
diff changeset
191 // broadcast compatible). The results need to be broadcast compatible with
anatofuz
parents:
diff changeset
192 // this result shape.
anatofuz
parents:
diff changeset
193 SmallVector<int64_t, 4> resultShape;
anatofuz
parents:
diff changeset
194 (void)util::getBroadcastedShape(getShape(*rankedOperands.begin()), {},
anatofuz
parents:
diff changeset
195 resultShape);
anatofuz
parents:
diff changeset
196 for (auto other : make_early_inc_range(rankedOperands)) {
anatofuz
parents:
diff changeset
197 SmallVector<int64_t, 4> temp = resultShape;
anatofuz
parents:
diff changeset
198 if (!util::getBroadcastedShape(temp, getShape(other), resultShape))
anatofuz
parents:
diff changeset
199 return op->emitOpError("operands don't have broadcast-compatible shapes");
anatofuz
parents:
diff changeset
200 }
anatofuz
parents:
diff changeset
201
anatofuz
parents:
diff changeset
202 auto rankedResults = make_filter_range(
anatofuz
parents:
diff changeset
203 op->getResultTypes(), [](Type t) { return t.isa<RankedTensorType>(); });
anatofuz
parents:
diff changeset
204
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
205 // If all of the results are unranked then no further verification.
150
anatofuz
parents:
diff changeset
206 if (rankedResults.empty())
anatofuz
parents:
diff changeset
207 return success();
anatofuz
parents:
diff changeset
208
anatofuz
parents:
diff changeset
209 for (auto type : rankedResults) {
anatofuz
parents:
diff changeset
210 ArrayRef<int64_t> actualSuffix =
anatofuz
parents:
diff changeset
211 getShape(type).take_back(resultShape.size());
anatofuz
parents:
diff changeset
212 if (!areCompatibleShapes(actualSuffix, resultShape))
anatofuz
parents:
diff changeset
213 return op->emitOpError()
anatofuz
parents:
diff changeset
214 << "result type " << getShapeString(getShape(type))
anatofuz
parents:
diff changeset
215 << " not broadcast compatible with broadcasted operands's shapes "
anatofuz
parents:
diff changeset
216 << getShapeString(resultShape);
anatofuz
parents:
diff changeset
217 }
anatofuz
parents:
diff changeset
218 return success();
anatofuz
parents:
diff changeset
219 }