File: | build/llvm-toolchain-snapshot-16~++20220904122748+c444af1c20b3/mlir/include/mlir/IR/Types.h |
Warning: | line 113, column 31 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===- DataLayoutInterfaces.cpp - Data Layout Interface Implementation ----===// | |||
2 | // | |||
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |||
4 | // See https://llvm.org/LICENSE.txt for license information. | |||
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |||
6 | // | |||
7 | //===----------------------------------------------------------------------===// | |||
8 | ||||
9 | #include "mlir/Interfaces/DataLayoutInterfaces.h" | |||
10 | #include "mlir/IR/BuiltinDialect.h" | |||
11 | #include "mlir/IR/BuiltinOps.h" | |||
12 | #include "mlir/IR/BuiltinTypes.h" | |||
13 | #include "mlir/IR/Operation.h" | |||
14 | ||||
15 | #include "llvm/ADT/TypeSwitch.h" | |||
16 | #include "llvm/Support/MathExtras.h" | |||
17 | ||||
18 | using namespace mlir; | |||
19 | ||||
20 | //===----------------------------------------------------------------------===// | |||
21 | // Default implementations | |||
22 | //===----------------------------------------------------------------------===// | |||
23 | ||||
24 | /// Reports that the given type is missing the data layout information and | |||
25 | /// exits. | |||
26 | [[noreturn]] static void reportMissingDataLayout(Type type) { | |||
27 | std::string message; | |||
28 | llvm::raw_string_ostream os(message); | |||
29 | os << "neither the scoping op nor the type class provide data layout " | |||
30 | "information for " | |||
31 | << type; | |||
32 | llvm::report_fatal_error(Twine(os.str())); | |||
33 | } | |||
34 | ||||
35 | /// Returns the bitwidth of the index type if specified in the param list. | |||
36 | /// Assumes 64-bit index otherwise. | |||
37 | static unsigned getIndexBitwidth(DataLayoutEntryListRef params) { | |||
38 | if (params.empty()) | |||
39 | return 64; | |||
40 | auto attr = params.front().getValue().cast<IntegerAttr>(); | |||
41 | return attr.getValue().getZExtValue(); | |||
42 | } | |||
43 | ||||
44 | unsigned | |||
45 | mlir::detail::getDefaultTypeSize(Type type, const DataLayout &dataLayout, | |||
46 | ArrayRef<DataLayoutEntryInterface> params) { | |||
47 | unsigned bits = getDefaultTypeSizeInBits(type, dataLayout, params); | |||
48 | return llvm::divideCeil(bits, 8); | |||
49 | } | |||
50 | ||||
51 | unsigned mlir::detail::getDefaultTypeSizeInBits(Type type, | |||
52 | const DataLayout &dataLayout, | |||
53 | DataLayoutEntryListRef params) { | |||
54 | if (type.isa<IntegerType, FloatType>()) | |||
55 | return type.getIntOrFloatBitWidth(); | |||
56 | ||||
57 | if (auto ctype = type.dyn_cast<ComplexType>()) { | |||
58 | auto et = ctype.getElementType(); | |||
59 | auto innerAlignment = | |||
60 | getDefaultPreferredAlignment(et, dataLayout, params) * 8; | |||
61 | auto innerSize = getDefaultTypeSizeInBits(et, dataLayout, params); | |||
62 | ||||
63 | // Include padding required to align the imaginary value in the complex | |||
64 | // type. | |||
65 | return llvm::alignTo(innerSize, innerAlignment) + innerSize; | |||
66 | } | |||
67 | ||||
68 | // Index is an integer of some bitwidth. | |||
69 | if (type.isa<IndexType>()) | |||
70 | return dataLayout.getTypeSizeInBits( | |||
71 | IntegerType::get(type.getContext(), getIndexBitwidth(params))); | |||
72 | ||||
73 | // Sizes of vector types are rounded up to those of types with closest | |||
74 | // power-of-two number of elements in the innermost dimension. We also assume | |||
75 | // there is no bit-packing at the moment element sizes are taken in bytes and | |||
76 | // multiplied with 8 bits. | |||
77 | // TODO: make this extensible. | |||
78 | if (auto vecType = type.dyn_cast<VectorType>()) | |||
79 | return vecType.getNumElements() / vecType.getShape().back() * | |||
80 | llvm::PowerOf2Ceil(vecType.getShape().back()) * | |||
81 | dataLayout.getTypeSize(vecType.getElementType()) * 8; | |||
82 | ||||
83 | if (auto typeInterface = type.dyn_cast<DataLayoutTypeInterface>()) | |||
84 | return typeInterface.getTypeSizeInBits(dataLayout, params); | |||
85 | ||||
86 | reportMissingDataLayout(type); | |||
87 | } | |||
88 | ||||
89 | static DataLayoutEntryInterface | |||
90 | findEntryForIntegerType(IntegerType intType, | |||
91 | ArrayRef<DataLayoutEntryInterface> params) { | |||
92 | assert(!params.empty() && "expected non-empty parameter list")(static_cast <bool> (!params.empty() && "expected non-empty parameter list" ) ? void (0) : __assert_fail ("!params.empty() && \"expected non-empty parameter list\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 92, __extension__ __PRETTY_FUNCTION__)); | |||
93 | std::map<unsigned, DataLayoutEntryInterface> sortedParams; | |||
94 | for (DataLayoutEntryInterface entry : params) { | |||
95 | sortedParams.insert(std::make_pair( | |||
96 | entry.getKey().get<Type>().getIntOrFloatBitWidth(), entry)); | |||
97 | } | |||
98 | auto iter = sortedParams.lower_bound(intType.getWidth()); | |||
99 | if (iter == sortedParams.end()) | |||
100 | iter = std::prev(iter); | |||
101 | ||||
102 | return iter->second; | |||
103 | } | |||
104 | ||||
105 | static unsigned extractABIAlignment(DataLayoutEntryInterface entry) { | |||
106 | auto values = | |||
107 | entry.getValue().cast<DenseIntElementsAttr>().getValues<int32_t>(); | |||
108 | return *values.begin() / 8u; | |||
109 | } | |||
110 | ||||
111 | static unsigned | |||
112 | getIntegerTypeABIAlignment(IntegerType intType, | |||
113 | ArrayRef<DataLayoutEntryInterface> params) { | |||
114 | if (params.empty()) { | |||
115 | return intType.getWidth() < 64 | |||
116 | ? llvm::PowerOf2Ceil(llvm::divideCeil(intType.getWidth(), 8)) | |||
117 | : 4; | |||
118 | } | |||
119 | ||||
120 | return extractABIAlignment(findEntryForIntegerType(intType, params)); | |||
121 | } | |||
122 | ||||
123 | static unsigned | |||
124 | getFloatTypeABIAlignment(FloatType fltType, const DataLayout &dataLayout, | |||
125 | ArrayRef<DataLayoutEntryInterface> params) { | |||
126 | assert(params.size() <= 1 && "at most one data layout entry is expected for "(static_cast <bool> (params.size() <= 1 && "at most one data layout entry is expected for " "the singleton floating-point type") ? void (0) : __assert_fail ("params.size() <= 1 && \"at most one data layout entry is expected for \" \"the singleton floating-point type\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 127, __extension__ __PRETTY_FUNCTION__)) | |||
127 | "the singleton floating-point type")(static_cast <bool> (params.size() <= 1 && "at most one data layout entry is expected for " "the singleton floating-point type") ? void (0) : __assert_fail ("params.size() <= 1 && \"at most one data layout entry is expected for \" \"the singleton floating-point type\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 127, __extension__ __PRETTY_FUNCTION__)); | |||
128 | if (params.empty()) | |||
129 | return llvm::PowerOf2Ceil(dataLayout.getTypeSize(fltType)); | |||
130 | return extractABIAlignment(params[0]); | |||
131 | } | |||
132 | ||||
133 | unsigned mlir::detail::getDefaultABIAlignment( | |||
134 | Type type, const DataLayout &dataLayout, | |||
135 | ArrayRef<DataLayoutEntryInterface> params) { | |||
136 | // Natural alignment is the closest power-of-two number above. | |||
137 | if (type.isa<VectorType>()) | |||
138 | return llvm::PowerOf2Ceil(dataLayout.getTypeSize(type)); | |||
139 | ||||
140 | if (auto fltType = type.dyn_cast<FloatType>()) | |||
141 | return getFloatTypeABIAlignment(fltType, dataLayout, params); | |||
142 | ||||
143 | // Index is an integer of some bitwidth. | |||
144 | if (type.isa<IndexType>()) | |||
145 | return dataLayout.getTypeABIAlignment( | |||
146 | IntegerType::get(type.getContext(), getIndexBitwidth(params))); | |||
147 | ||||
148 | if (auto intType = type.dyn_cast<IntegerType>()) | |||
149 | return getIntegerTypeABIAlignment(intType, params); | |||
150 | ||||
151 | if (auto ctype = type.dyn_cast<ComplexType>()) | |||
152 | return getDefaultABIAlignment(ctype.getElementType(), dataLayout, params); | |||
153 | ||||
154 | if (auto typeInterface = type.dyn_cast<DataLayoutTypeInterface>()) | |||
155 | return typeInterface.getABIAlignment(dataLayout, params); | |||
156 | ||||
157 | reportMissingDataLayout(type); | |||
158 | } | |||
159 | ||||
160 | static unsigned extractPreferredAlignment(DataLayoutEntryInterface entry) { | |||
161 | auto values = | |||
162 | entry.getValue().cast<DenseIntElementsAttr>().getValues<int32_t>(); | |||
163 | return *std::next(values.begin(), values.size() - 1) / 8u; | |||
164 | } | |||
165 | ||||
166 | static unsigned | |||
167 | getIntegerTypePreferredAlignment(IntegerType intType, | |||
168 | const DataLayout &dataLayout, | |||
169 | ArrayRef<DataLayoutEntryInterface> params) { | |||
170 | if (params.empty()) | |||
171 | return llvm::PowerOf2Ceil(dataLayout.getTypeSize(intType)); | |||
172 | ||||
173 | return extractPreferredAlignment(findEntryForIntegerType(intType, params)); | |||
174 | } | |||
175 | ||||
176 | static unsigned | |||
177 | getFloatTypePreferredAlignment(FloatType fltType, const DataLayout &dataLayout, | |||
178 | ArrayRef<DataLayoutEntryInterface> params) { | |||
179 | assert(params.size() <= 1 && "at most one data layout entry is expected for "(static_cast <bool> (params.size() <= 1 && "at most one data layout entry is expected for " "the singleton floating-point type") ? void (0) : __assert_fail ("params.size() <= 1 && \"at most one data layout entry is expected for \" \"the singleton floating-point type\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 180, __extension__ __PRETTY_FUNCTION__)) | |||
180 | "the singleton floating-point type")(static_cast <bool> (params.size() <= 1 && "at most one data layout entry is expected for " "the singleton floating-point type") ? void (0) : __assert_fail ("params.size() <= 1 && \"at most one data layout entry is expected for \" \"the singleton floating-point type\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 180, __extension__ __PRETTY_FUNCTION__)); | |||
181 | if (params.empty()) | |||
182 | return dataLayout.getTypeABIAlignment(fltType); | |||
183 | return extractPreferredAlignment(params[0]); | |||
184 | } | |||
185 | ||||
186 | unsigned mlir::detail::getDefaultPreferredAlignment( | |||
187 | Type type, const DataLayout &dataLayout, | |||
188 | ArrayRef<DataLayoutEntryInterface> params) { | |||
189 | // Preferred alignment is same as natural for floats and vectors. | |||
190 | if (type.isa<VectorType>()) | |||
191 | return dataLayout.getTypeABIAlignment(type); | |||
192 | ||||
193 | if (auto fltType = type.dyn_cast<FloatType>()) | |||
194 | return getFloatTypePreferredAlignment(fltType, dataLayout, params); | |||
195 | ||||
196 | // Preferred alignment is the closest power-of-two number above for integers | |||
197 | // (ABI alignment may be smaller). | |||
198 | if (auto intType = type.dyn_cast<IntegerType>()) | |||
199 | return getIntegerTypePreferredAlignment(intType, dataLayout, params); | |||
200 | ||||
201 | if (type.isa<IndexType>()) { | |||
202 | return dataLayout.getTypePreferredAlignment( | |||
203 | IntegerType::get(type.getContext(), getIndexBitwidth(params))); | |||
204 | } | |||
205 | ||||
206 | if (auto ctype = type.dyn_cast<ComplexType>()) | |||
207 | return getDefaultPreferredAlignment(ctype.getElementType(), dataLayout, | |||
208 | params); | |||
209 | ||||
210 | if (auto typeInterface = type.dyn_cast<DataLayoutTypeInterface>()) | |||
211 | return typeInterface.getPreferredAlignment(dataLayout, params); | |||
212 | ||||
213 | reportMissingDataLayout(type); | |||
214 | } | |||
215 | ||||
216 | DataLayoutEntryList | |||
217 | mlir::detail::filterEntriesForType(DataLayoutEntryListRef entries, | |||
218 | TypeID typeID) { | |||
219 | return llvm::to_vector<4>(llvm::make_filter_range( | |||
220 | entries, [typeID](DataLayoutEntryInterface entry) { | |||
221 | auto type = entry.getKey().dyn_cast<Type>(); | |||
222 | return type && type.getTypeID() == typeID; | |||
223 | })); | |||
224 | } | |||
225 | ||||
226 | DataLayoutEntryInterface | |||
227 | mlir::detail::filterEntryForIdentifier(DataLayoutEntryListRef entries, | |||
228 | StringAttr id) { | |||
229 | const auto *it = llvm::find_if(entries, [id](DataLayoutEntryInterface entry) { | |||
230 | if (!entry.getKey().is<StringAttr>()) | |||
231 | return false; | |||
232 | return entry.getKey().get<StringAttr>() == id; | |||
233 | }); | |||
234 | return it == entries.end() ? DataLayoutEntryInterface() : *it; | |||
235 | } | |||
236 | ||||
237 | static DataLayoutSpecInterface getSpec(Operation *operation) { | |||
238 | return llvm::TypeSwitch<Operation *, DataLayoutSpecInterface>(operation) | |||
239 | .Case<ModuleOp, DataLayoutOpInterface>( | |||
240 | [&](auto op) { return op.getDataLayoutSpec(); }) | |||
241 | .Default([](Operation *) { | |||
242 | llvm_unreachable("expected an op with data layout spec")::llvm::llvm_unreachable_internal("expected an op with data layout spec" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 242); | |||
243 | return DataLayoutSpecInterface(); | |||
244 | }); | |||
245 | } | |||
246 | ||||
247 | /// Populates `opsWithLayout` with the list of proper ancestors of `leaf` that | |||
248 | /// are either modules or implement the `DataLayoutOpInterface`. | |||
249 | static void | |||
250 | collectParentLayouts(Operation *leaf, | |||
251 | SmallVectorImpl<DataLayoutSpecInterface> &specs, | |||
252 | SmallVectorImpl<Location> *opLocations = nullptr) { | |||
253 | if (!leaf) | |||
254 | return; | |||
255 | ||||
256 | for (Operation *parent = leaf->getParentOp(); parent != nullptr; | |||
257 | parent = parent->getParentOp()) { | |||
258 | llvm::TypeSwitch<Operation *>(parent) | |||
259 | .Case<ModuleOp>([&](ModuleOp op) { | |||
260 | // Skip top-level module op unless it has a layout. Top-level module | |||
261 | // without layout is most likely the one implicitly added by the | |||
262 | // parser and it doesn't have location. Top-level null specification | |||
263 | // would have had the same effect as not having a specification at all | |||
264 | // (using type defaults). | |||
265 | if (!op->getParentOp() && !op.getDataLayoutSpec()) | |||
266 | return; | |||
267 | specs.push_back(op.getDataLayoutSpec()); | |||
268 | if (opLocations) | |||
269 | opLocations->push_back(op.getLoc()); | |||
270 | }) | |||
271 | .Case<DataLayoutOpInterface>([&](DataLayoutOpInterface op) { | |||
272 | specs.push_back(op.getDataLayoutSpec()); | |||
273 | if (opLocations) | |||
274 | opLocations->push_back(op.getLoc()); | |||
275 | }); | |||
276 | } | |||
277 | } | |||
278 | ||||
279 | /// Returns a layout spec that is a combination of the layout specs attached | |||
280 | /// to the given operation and all its ancestors. | |||
281 | static DataLayoutSpecInterface getCombinedDataLayout(Operation *leaf) { | |||
282 | if (!leaf) | |||
283 | return {}; | |||
284 | ||||
285 | assert((isa<ModuleOp, DataLayoutOpInterface>(leaf)) &&(static_cast <bool> ((isa<ModuleOp, DataLayoutOpInterface >(leaf)) && "expected an op with data layout spec" ) ? void (0) : __assert_fail ("(isa<ModuleOp, DataLayoutOpInterface>(leaf)) && \"expected an op with data layout spec\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 286, __extension__ __PRETTY_FUNCTION__)) | |||
286 | "expected an op with data layout spec")(static_cast <bool> ((isa<ModuleOp, DataLayoutOpInterface >(leaf)) && "expected an op with data layout spec" ) ? void (0) : __assert_fail ("(isa<ModuleOp, DataLayoutOpInterface>(leaf)) && \"expected an op with data layout spec\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 286, __extension__ __PRETTY_FUNCTION__)); | |||
287 | ||||
288 | SmallVector<DataLayoutOpInterface> opsWithLayout; | |||
289 | SmallVector<DataLayoutSpecInterface> specs; | |||
290 | collectParentLayouts(leaf, specs); | |||
291 | ||||
292 | // Fast track if there are no ancestors. | |||
293 | if (specs.empty()) | |||
294 | return getSpec(leaf); | |||
295 | ||||
296 | // Create the list of non-null specs (null/missing specs can be safely | |||
297 | // ignored) from the outermost to the innermost. | |||
298 | auto nonNullSpecs = llvm::to_vector<2>(llvm::make_filter_range( | |||
299 | llvm::reverse(specs), | |||
300 | [](DataLayoutSpecInterface iface) { return iface != nullptr; })); | |||
301 | ||||
302 | // Combine the specs using the innermost as anchor. | |||
303 | if (DataLayoutSpecInterface current = getSpec(leaf)) | |||
304 | return current.combineWith(nonNullSpecs); | |||
305 | if (nonNullSpecs.empty()) | |||
306 | return {}; | |||
307 | return nonNullSpecs.back().combineWith( | |||
308 | llvm::makeArrayRef(nonNullSpecs).drop_back()); | |||
309 | } | |||
310 | ||||
311 | LogicalResult mlir::detail::verifyDataLayoutOp(Operation *op) { | |||
312 | DataLayoutSpecInterface spec = getSpec(op); | |||
313 | // The layout specification may be missing and it's fine. | |||
314 | if (!spec) | |||
315 | return success(); | |||
316 | ||||
317 | if (failed(spec.verifySpec(op->getLoc()))) | |||
318 | return failure(); | |||
319 | if (!getCombinedDataLayout(op)) { | |||
320 | InFlightDiagnostic diag = | |||
321 | op->emitError() | |||
322 | << "data layout does not combine with layouts of enclosing ops"; | |||
323 | SmallVector<DataLayoutSpecInterface> specs; | |||
324 | SmallVector<Location> opLocations; | |||
325 | collectParentLayouts(op, specs, &opLocations); | |||
326 | for (Location loc : opLocations) | |||
327 | diag.attachNote(loc) << "enclosing op with data layout"; | |||
328 | return diag; | |||
329 | } | |||
330 | return success(); | |||
331 | } | |||
332 | ||||
333 | //===----------------------------------------------------------------------===// | |||
334 | // DataLayout | |||
335 | //===----------------------------------------------------------------------===// | |||
336 | ||||
337 | template <typename OpTy> | |||
338 | void checkMissingLayout(DataLayoutSpecInterface originalLayout, OpTy op) { | |||
339 | if (!originalLayout) { | |||
340 | assert((!op || !op.getDataLayoutSpec()) &&(static_cast <bool> ((!op || !op.getDataLayoutSpec()) && "could not compute layout information for an op (failed to " "combine attributes?)") ? void (0) : __assert_fail ("(!op || !op.getDataLayoutSpec()) && \"could not compute layout information for an op (failed to \" \"combine attributes?)\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 342, __extension__ __PRETTY_FUNCTION__)) | |||
341 | "could not compute layout information for an op (failed to "(static_cast <bool> ((!op || !op.getDataLayoutSpec()) && "could not compute layout information for an op (failed to " "combine attributes?)") ? void (0) : __assert_fail ("(!op || !op.getDataLayoutSpec()) && \"could not compute layout information for an op (failed to \" \"combine attributes?)\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 342, __extension__ __PRETTY_FUNCTION__)) | |||
342 | "combine attributes?)")(static_cast <bool> ((!op || !op.getDataLayoutSpec()) && "could not compute layout information for an op (failed to " "combine attributes?)") ? void (0) : __assert_fail ("(!op || !op.getDataLayoutSpec()) && \"could not compute layout information for an op (failed to \" \"combine attributes?)\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 342, __extension__ __PRETTY_FUNCTION__)); | |||
343 | } | |||
344 | } | |||
345 | ||||
346 | mlir::DataLayout::DataLayout() : DataLayout(ModuleOp()) {} | |||
347 | ||||
348 | mlir::DataLayout::DataLayout(DataLayoutOpInterface op) | |||
349 | : originalLayout(getCombinedDataLayout(op)), scope(op) { | |||
350 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 | |||
351 | checkMissingLayout(originalLayout, op); | |||
352 | collectParentLayouts(op, layoutStack); | |||
353 | #endif | |||
354 | } | |||
355 | ||||
356 | mlir::DataLayout::DataLayout(ModuleOp op) | |||
357 | : originalLayout(getCombinedDataLayout(op)), scope(op) { | |||
358 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 | |||
359 | checkMissingLayout(originalLayout, op); | |||
360 | collectParentLayouts(op, layoutStack); | |||
361 | #endif | |||
362 | } | |||
363 | ||||
364 | mlir::DataLayout mlir::DataLayout::closest(Operation *op) { | |||
365 | // Search the closest parent either being a module operation or implementing | |||
366 | // the data layout interface. | |||
367 | while (op) { | |||
368 | if (auto module = dyn_cast<ModuleOp>(op)) | |||
369 | return DataLayout(module); | |||
370 | if (auto iface = dyn_cast<DataLayoutOpInterface>(op)) | |||
371 | return DataLayout(iface); | |||
372 | op = op->getParentOp(); | |||
373 | } | |||
374 | return DataLayout(); | |||
375 | } | |||
376 | ||||
377 | void mlir::DataLayout::checkValid() const { | |||
378 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 | |||
379 | SmallVector<DataLayoutSpecInterface> specs; | |||
380 | collectParentLayouts(scope, specs); | |||
381 | assert(specs.size() == layoutStack.size() &&(static_cast <bool> (specs.size() == layoutStack.size() && "data layout object used, but no longer valid due to the change in " "number of nested layouts") ? void (0) : __assert_fail ("specs.size() == layoutStack.size() && \"data layout object used, but no longer valid due to the change in \" \"number of nested layouts\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 383, __extension__ __PRETTY_FUNCTION__)) | |||
382 | "data layout object used, but no longer valid due to the change in "(static_cast <bool> (specs.size() == layoutStack.size() && "data layout object used, but no longer valid due to the change in " "number of nested layouts") ? void (0) : __assert_fail ("specs.size() == layoutStack.size() && \"data layout object used, but no longer valid due to the change in \" \"number of nested layouts\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 383, __extension__ __PRETTY_FUNCTION__)) | |||
383 | "number of nested layouts")(static_cast <bool> (specs.size() == layoutStack.size() && "data layout object used, but no longer valid due to the change in " "number of nested layouts") ? void (0) : __assert_fail ("specs.size() == layoutStack.size() && \"data layout object used, but no longer valid due to the change in \" \"number of nested layouts\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 383, __extension__ __PRETTY_FUNCTION__)); | |||
384 | for (auto pair : llvm::zip(specs, layoutStack)) { | |||
385 | Attribute newLayout = std::get<0>(pair); | |||
386 | Attribute origLayout = std::get<1>(pair); | |||
387 | assert(newLayout == origLayout &&(static_cast <bool> (newLayout == origLayout && "data layout object used, but no longer valid " "due to the change in layout attributes" ) ? void (0) : __assert_fail ("newLayout == origLayout && \"data layout object used, but no longer valid \" \"due to the change in layout attributes\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 389, __extension__ __PRETTY_FUNCTION__)) | |||
388 | "data layout object used, but no longer valid "(static_cast <bool> (newLayout == origLayout && "data layout object used, but no longer valid " "due to the change in layout attributes" ) ? void (0) : __assert_fail ("newLayout == origLayout && \"data layout object used, but no longer valid \" \"due to the change in layout attributes\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 389, __extension__ __PRETTY_FUNCTION__)) | |||
389 | "due to the change in layout attributes")(static_cast <bool> (newLayout == origLayout && "data layout object used, but no longer valid " "due to the change in layout attributes" ) ? void (0) : __assert_fail ("newLayout == origLayout && \"data layout object used, but no longer valid \" \"due to the change in layout attributes\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 389, __extension__ __PRETTY_FUNCTION__)); | |||
390 | } | |||
391 | #endif | |||
392 | assert(((!scope && !this->originalLayout) ||(static_cast <bool> (((!scope && !this->originalLayout ) || (scope && this->originalLayout == getCombinedDataLayout (scope))) && "data layout object used, but no longer valid due to the change in " "layout spec") ? void (0) : __assert_fail ("((!scope && !this->originalLayout) || (scope && this->originalLayout == getCombinedDataLayout(scope))) && \"data layout object used, but no longer valid due to the change in \" \"layout spec\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 395, __extension__ __PRETTY_FUNCTION__)) | |||
393 | (scope && this->originalLayout == getCombinedDataLayout(scope))) &&(static_cast <bool> (((!scope && !this->originalLayout ) || (scope && this->originalLayout == getCombinedDataLayout (scope))) && "data layout object used, but no longer valid due to the change in " "layout spec") ? void (0) : __assert_fail ("((!scope && !this->originalLayout) || (scope && this->originalLayout == getCombinedDataLayout(scope))) && \"data layout object used, but no longer valid due to the change in \" \"layout spec\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 395, __extension__ __PRETTY_FUNCTION__)) | |||
394 | "data layout object used, but no longer valid due to the change in "(static_cast <bool> (((!scope && !this->originalLayout ) || (scope && this->originalLayout == getCombinedDataLayout (scope))) && "data layout object used, but no longer valid due to the change in " "layout spec") ? void (0) : __assert_fail ("((!scope && !this->originalLayout) || (scope && this->originalLayout == getCombinedDataLayout(scope))) && \"data layout object used, but no longer valid due to the change in \" \"layout spec\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 395, __extension__ __PRETTY_FUNCTION__)) | |||
395 | "layout spec")(static_cast <bool> (((!scope && !this->originalLayout ) || (scope && this->originalLayout == getCombinedDataLayout (scope))) && "data layout object used, but no longer valid due to the change in " "layout spec") ? void (0) : __assert_fail ("((!scope && !this->originalLayout) || (scope && this->originalLayout == getCombinedDataLayout(scope))) && \"data layout object used, but no longer valid due to the change in \" \"layout spec\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 395, __extension__ __PRETTY_FUNCTION__)); | |||
396 | } | |||
397 | ||||
398 | /// Looks up the value for the given type key in the given cache. If there is no | |||
399 | /// such value in the cache, compute it using the given callback and put it in | |||
400 | /// the cache before returning. | |||
401 | static unsigned cachedLookup(Type t, DenseMap<Type, unsigned> &cache, | |||
402 | function_ref<unsigned(Type)> compute) { | |||
403 | auto it = cache.find(t); | |||
404 | if (it != cache.end()) | |||
405 | return it->second; | |||
406 | ||||
407 | auto result = cache.try_emplace(t, compute(t)); | |||
408 | return result.first->second; | |||
409 | } | |||
410 | ||||
411 | unsigned mlir::DataLayout::getTypeSize(Type t) const { | |||
412 | checkValid(); | |||
413 | return cachedLookup(t, sizes, [&](Type ty) { | |||
414 | DataLayoutEntryList list; | |||
415 | if (originalLayout) | |||
416 | list = originalLayout.getSpecForType(ty.getTypeID()); | |||
417 | if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope)) | |||
418 | return iface.getTypeSize(ty, *this, list); | |||
419 | return detail::getDefaultTypeSize(ty, *this, list); | |||
420 | }); | |||
421 | } | |||
422 | ||||
423 | unsigned mlir::DataLayout::getTypeSizeInBits(Type t) const { | |||
424 | checkValid(); | |||
425 | return cachedLookup(t, bitsizes, [&](Type ty) { | |||
426 | DataLayoutEntryList list; | |||
427 | if (originalLayout) | |||
428 | list = originalLayout.getSpecForType(ty.getTypeID()); | |||
429 | if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope)) | |||
430 | return iface.getTypeSizeInBits(ty, *this, list); | |||
431 | return detail::getDefaultTypeSizeInBits(ty, *this, list); | |||
432 | }); | |||
433 | } | |||
434 | ||||
435 | unsigned mlir::DataLayout::getTypeABIAlignment(Type t) const { | |||
436 | checkValid(); | |||
437 | return cachedLookup(t, abiAlignments, [&](Type ty) { | |||
438 | DataLayoutEntryList list; | |||
439 | if (originalLayout) | |||
440 | list = originalLayout.getSpecForType(ty.getTypeID()); | |||
441 | if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope)) | |||
442 | return iface.getTypeABIAlignment(ty, *this, list); | |||
443 | return detail::getDefaultABIAlignment(ty, *this, list); | |||
444 | }); | |||
445 | } | |||
446 | ||||
447 | unsigned mlir::DataLayout::getTypePreferredAlignment(Type t) const { | |||
448 | checkValid(); | |||
449 | return cachedLookup(t, preferredAlignments, [&](Type ty) { | |||
450 | DataLayoutEntryList list; | |||
451 | if (originalLayout) | |||
| ||||
452 | list = originalLayout.getSpecForType(ty.getTypeID()); | |||
453 | if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope)) | |||
454 | return iface.getTypePreferredAlignment(ty, *this, list); | |||
455 | return detail::getDefaultPreferredAlignment(ty, *this, list); | |||
456 | }); | |||
457 | } | |||
458 | ||||
459 | //===----------------------------------------------------------------------===// | |||
460 | // DataLayoutSpecInterface | |||
461 | //===----------------------------------------------------------------------===// | |||
462 | ||||
463 | void DataLayoutSpecInterface::bucketEntriesByType( | |||
464 | DenseMap<TypeID, DataLayoutEntryList> &types, | |||
465 | DenseMap<StringAttr, DataLayoutEntryInterface> &ids) { | |||
466 | for (DataLayoutEntryInterface entry : getEntries()) { | |||
467 | if (auto type = entry.getKey().dyn_cast<Type>()) | |||
468 | types[type.getTypeID()].push_back(entry); | |||
469 | else | |||
470 | ids[entry.getKey().get<StringAttr>()] = entry; | |||
471 | } | |||
472 | } | |||
473 | ||||
474 | LogicalResult mlir::detail::verifyDataLayoutSpec(DataLayoutSpecInterface spec, | |||
475 | Location loc) { | |||
476 | // First, verify individual entries. | |||
477 | for (DataLayoutEntryInterface entry : spec.getEntries()) | |||
478 | if (failed(entry.verifyEntry(loc))) | |||
479 | return failure(); | |||
480 | ||||
481 | // Second, dispatch verifications of entry groups to types or dialects they | |||
482 | // are are associated with. | |||
483 | DenseMap<TypeID, DataLayoutEntryList> types; | |||
484 | DenseMap<StringAttr, DataLayoutEntryInterface> ids; | |||
485 | spec.bucketEntriesByType(types, ids); | |||
486 | ||||
487 | for (const auto &kvp : types) { | |||
488 | auto sampleType = kvp.second.front().getKey().get<Type>(); | |||
489 | if (sampleType.isa<IndexType>()) { | |||
490 | assert(kvp.second.size() == 1 &&(static_cast <bool> (kvp.second.size() == 1 && "expected one data layout entry for non-parametric 'index' type" ) ? void (0) : __assert_fail ("kvp.second.size() == 1 && \"expected one data layout entry for non-parametric 'index' type\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 491, __extension__ __PRETTY_FUNCTION__)) | |||
491 | "expected one data layout entry for non-parametric 'index' type")(static_cast <bool> (kvp.second.size() == 1 && "expected one data layout entry for non-parametric 'index' type" ) ? void (0) : __assert_fail ("kvp.second.size() == 1 && \"expected one data layout entry for non-parametric 'index' type\"" , "mlir/lib/Interfaces/DataLayoutInterfaces.cpp", 491, __extension__ __PRETTY_FUNCTION__)); | |||
492 | if (!kvp.second.front().getValue().isa<IntegerAttr>()) | |||
493 | return emitError(loc) | |||
494 | << "expected integer attribute in the data layout entry for " | |||
495 | << sampleType; | |||
496 | continue; | |||
497 | } | |||
498 | ||||
499 | if (sampleType.isa<IntegerType, FloatType>()) { | |||
500 | for (DataLayoutEntryInterface entry : kvp.second) { | |||
501 | auto value = entry.getValue().dyn_cast<DenseIntElementsAttr>(); | |||
502 | if (!value || !value.getElementType().isSignlessInteger(32)) { | |||
503 | emitError(loc) << "expected a dense i32 elements attribute in the " | |||
504 | "data layout entry " | |||
505 | << entry; | |||
506 | return failure(); | |||
507 | } | |||
508 | ||||
509 | auto elements = llvm::to_vector<2>(value.getValues<int32_t>()); | |||
510 | unsigned numElements = elements.size(); | |||
511 | if (numElements < 1 || numElements > 2) { | |||
512 | emitError(loc) << "expected 1 or 2 elements in the data layout entry " | |||
513 | << entry; | |||
514 | return failure(); | |||
515 | } | |||
516 | ||||
517 | int32_t abi = elements[0]; | |||
518 | int32_t preferred = numElements == 2 ? elements[1] : abi; | |||
519 | if (preferred < abi) { | |||
520 | emitError(loc) | |||
521 | << "preferred alignment is expected to be greater than or equal " | |||
522 | "to the abi alignment in data layout entry " | |||
523 | << entry; | |||
524 | return failure(); | |||
525 | } | |||
526 | } | |||
527 | continue; | |||
528 | } | |||
529 | ||||
530 | if (isa<BuiltinDialect>(&sampleType.getDialect())) | |||
531 | return emitError(loc) << "unexpected data layout for a built-in type"; | |||
532 | ||||
533 | auto dlType = sampleType.dyn_cast<DataLayoutTypeInterface>(); | |||
534 | if (!dlType) | |||
535 | return emitError(loc) | |||
536 | << "data layout specified for a type that does not support it"; | |||
537 | if (failed(dlType.verifyEntries(kvp.second, loc))) | |||
538 | return failure(); | |||
539 | } | |||
540 | ||||
541 | for (const auto &kvp : ids) { | |||
542 | StringAttr identifier = kvp.second.getKey().get<StringAttr>(); | |||
543 | Dialect *dialect = identifier.getReferencedDialect(); | |||
544 | ||||
545 | // Ignore attributes that belong to an unknown dialect, the dialect may | |||
546 | // actually implement the relevant interface but we don't know about that. | |||
547 | if (!dialect) | |||
548 | continue; | |||
549 | ||||
550 | const auto *iface = dyn_cast<DataLayoutDialectInterface>(dialect); | |||
551 | if (!iface) { | |||
552 | return emitError(loc) | |||
553 | << "the '" << dialect->getNamespace() | |||
554 | << "' dialect does not support identifier data layout entries"; | |||
555 | } | |||
556 | if (failed(iface->verifyEntry(kvp.second, loc))) | |||
557 | return failure(); | |||
558 | } | |||
559 | ||||
560 | return success(); | |||
561 | } | |||
562 | ||||
563 | #include "mlir/Interfaces/DataLayoutAttrInterface.cpp.inc" | |||
564 | #include "mlir/Interfaces/DataLayoutOpInterface.cpp.inc" | |||
565 | #include "mlir/Interfaces/DataLayoutTypeInterface.cpp.inc" |
1 | //===- Types.h - MLIR Type Classes ------------------------------*- C++ -*-===// | ||||||||
2 | // | ||||||||
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||||||||
4 | // See https://llvm.org/LICENSE.txt for license information. | ||||||||
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||||||||
6 | // | ||||||||
7 | //===----------------------------------------------------------------------===// | ||||||||
8 | |||||||||
9 | #ifndef MLIR_IR_TYPES_H | ||||||||
10 | #define MLIR_IR_TYPES_H | ||||||||
11 | |||||||||
12 | #include "mlir/IR/TypeSupport.h" | ||||||||
13 | #include "llvm/ADT/ArrayRef.h" | ||||||||
14 | #include "llvm/ADT/DenseMapInfo.h" | ||||||||
15 | #include "llvm/Support/PointerLikeTypeTraits.h" | ||||||||
16 | |||||||||
17 | namespace mlir { | ||||||||
18 | /// Instances of the Type class are uniqued, have an immutable identifier and an | ||||||||
19 | /// optional mutable component. They wrap a pointer to the storage object owned | ||||||||
20 | /// by MLIRContext. Therefore, instances of Type are passed around by value. | ||||||||
21 | /// | ||||||||
22 | /// Some types are "primitives" meaning they do not have any parameters, for | ||||||||
23 | /// example the Index type. Parametric types have additional information that | ||||||||
24 | /// differentiates the types of the same class, for example the Integer type has | ||||||||
25 | /// bitwidth, making i8 and i16 belong to the same kind by be different | ||||||||
26 | /// instances of the IntegerType. Type parameters are part of the unique | ||||||||
27 | /// immutable key. The mutable component of the type can be modified after the | ||||||||
28 | /// type is created, but cannot affect the identity of the type. | ||||||||
29 | /// | ||||||||
30 | /// Types are constructed and uniqued via the 'detail::TypeUniquer' class. | ||||||||
31 | /// | ||||||||
32 | /// Derived type classes are expected to implement several required | ||||||||
33 | /// implementation hooks: | ||||||||
34 | /// * Optional: | ||||||||
35 | /// - static LogicalResult verify( | ||||||||
36 | /// function_ref<InFlightDiagnostic()> emitError, | ||||||||
37 | /// Args... args) | ||||||||
38 | /// * This method is invoked when calling the 'TypeBase::get/getChecked' | ||||||||
39 | /// methods to ensure that the arguments passed in are valid to construct | ||||||||
40 | /// a type instance with. | ||||||||
41 | /// * This method is expected to return failure if a type cannot be | ||||||||
42 | /// constructed with 'args', success otherwise. | ||||||||
43 | /// * 'args' must correspond with the arguments passed into the | ||||||||
44 | /// 'TypeBase::get' call. | ||||||||
45 | /// | ||||||||
46 | /// | ||||||||
47 | /// Type storage objects inherit from TypeStorage and contain the following: | ||||||||
48 | /// - The dialect that defined the type. | ||||||||
49 | /// - Any parameters of the type. | ||||||||
50 | /// - An optional mutable component. | ||||||||
51 | /// For non-parametric types, a convenience DefaultTypeStorage is provided. | ||||||||
52 | /// Parametric storage types must derive TypeStorage and respect the following: | ||||||||
53 | /// - Define a type alias, KeyTy, to a type that uniquely identifies the | ||||||||
54 | /// instance of the type. | ||||||||
55 | /// * The key type must be constructible from the values passed into the | ||||||||
56 | /// detail::TypeUniquer::get call. | ||||||||
57 | /// * If the KeyTy does not have an llvm::DenseMapInfo specialization, the | ||||||||
58 | /// storage class must define a hashing method: | ||||||||
59 | /// 'static unsigned hashKey(const KeyTy &)' | ||||||||
60 | /// | ||||||||
61 | /// - Provide a method, 'bool operator==(const KeyTy &) const', to | ||||||||
62 | /// compare the storage instance against an instance of the key type. | ||||||||
63 | /// | ||||||||
64 | /// - Provide a static construction method: | ||||||||
65 | /// 'DerivedStorage *construct(TypeStorageAllocator &, const KeyTy &key)' | ||||||||
66 | /// that builds a unique instance of the derived storage. The arguments to | ||||||||
67 | /// this function are an allocator to store any uniqued data within the | ||||||||
68 | /// context and the key type for this storage. | ||||||||
69 | /// | ||||||||
70 | /// - If they have a mutable component, this component must not be a part of | ||||||||
71 | /// the key. | ||||||||
72 | class Type { | ||||||||
73 | public: | ||||||||
74 | /// Utility class for implementing types. | ||||||||
75 | template <typename ConcreteType, typename BaseType, typename StorageType, | ||||||||
76 | template <typename T> class... Traits> | ||||||||
77 | using TypeBase = detail::StorageUserBase<ConcreteType, BaseType, StorageType, | ||||||||
78 | detail::TypeUniquer, Traits...>; | ||||||||
79 | |||||||||
80 | using ImplType = TypeStorage; | ||||||||
81 | |||||||||
82 | using AbstractTy = AbstractType; | ||||||||
83 | |||||||||
84 | constexpr Type() {} | ||||||||
85 | /* implicit */ Type(const ImplType *impl) | ||||||||
86 | : impl(const_cast<ImplType *>(impl)) {} | ||||||||
87 | |||||||||
88 | Type(const Type &other) = default; | ||||||||
89 | Type &operator=(const Type &other) = default; | ||||||||
90 | |||||||||
91 | bool operator==(Type other) const { return impl == other.impl; } | ||||||||
92 | bool operator!=(Type other) const { return !(*this == other); } | ||||||||
93 | explicit operator bool() const { return impl; } | ||||||||
94 | |||||||||
95 | bool operator!() const { return impl == nullptr; } | ||||||||
96 | |||||||||
97 | template <typename... Tys> | ||||||||
98 | bool isa() const; | ||||||||
99 | template <typename... Tys> | ||||||||
100 | bool isa_and_nonnull() const; | ||||||||
101 | template <typename U> | ||||||||
102 | U dyn_cast() const; | ||||||||
103 | template <typename U> | ||||||||
104 | U dyn_cast_or_null() const; | ||||||||
105 | template <typename U> | ||||||||
106 | U cast() const; | ||||||||
107 | |||||||||
108 | // Support type casting Type to itself. | ||||||||
109 | static bool classof(Type) { return true; } | ||||||||
110 | |||||||||
111 | /// Return a unique identifier for the concrete type. This is used to support | ||||||||
112 | /// dynamic type casting. | ||||||||
113 | TypeID getTypeID() { return impl->getAbstractType().getTypeID(); } | ||||||||
| |||||||||
114 | |||||||||
115 | /// Return the MLIRContext in which this type was uniqued. | ||||||||
116 | MLIRContext *getContext() const; | ||||||||
117 | |||||||||
118 | /// Get the dialect this type is registered to. | ||||||||
119 | Dialect &getDialect() const { return impl->getAbstractType().getDialect(); } | ||||||||
120 | |||||||||
121 | // Convenience predicates. This is only for floating point types, | ||||||||
122 | // derived types should use isa/dyn_cast. | ||||||||
123 | bool isIndex() const; | ||||||||
124 | bool isBF16() const; | ||||||||
125 | bool isF16() const; | ||||||||
126 | bool isF32() const; | ||||||||
127 | bool isF64() const; | ||||||||
128 | bool isF80() const; | ||||||||
129 | bool isF128() const; | ||||||||
130 | |||||||||
131 | /// Return true if this is an integer type with the specified width. | ||||||||
132 | bool isInteger(unsigned width) const; | ||||||||
133 | /// Return true if this is a signless integer type (with the specified width). | ||||||||
134 | bool isSignlessInteger() const; | ||||||||
135 | bool isSignlessInteger(unsigned width) const; | ||||||||
136 | /// Return true if this is a signed integer type (with the specified width). | ||||||||
137 | bool isSignedInteger() const; | ||||||||
138 | bool isSignedInteger(unsigned width) const; | ||||||||
139 | /// Return true if this is an unsigned integer type (with the specified | ||||||||
140 | /// width). | ||||||||
141 | bool isUnsignedInteger() const; | ||||||||
142 | bool isUnsignedInteger(unsigned width) const; | ||||||||
143 | |||||||||
144 | /// Return the bit width of an integer or a float type, assert failure on | ||||||||
145 | /// other types. | ||||||||
146 | unsigned getIntOrFloatBitWidth() const; | ||||||||
147 | |||||||||
148 | /// Return true if this is a signless integer or index type. | ||||||||
149 | bool isSignlessIntOrIndex() const; | ||||||||
150 | /// Return true if this is a signless integer, index, or float type. | ||||||||
151 | bool isSignlessIntOrIndexOrFloat() const; | ||||||||
152 | /// Return true of this is a signless integer or a float type. | ||||||||
153 | bool isSignlessIntOrFloat() const; | ||||||||
154 | |||||||||
155 | /// Return true if this is an integer (of any signedness) or an index type. | ||||||||
156 | bool isIntOrIndex() const; | ||||||||
157 | /// Return true if this is an integer (of any signedness) or a float type. | ||||||||
158 | bool isIntOrFloat() const; | ||||||||
159 | /// Return true if this is an integer (of any signedness), index, or float | ||||||||
160 | /// type. | ||||||||
161 | bool isIntOrIndexOrFloat() const; | ||||||||
162 | |||||||||
163 | /// Print the current type. | ||||||||
164 | void print(raw_ostream &os) const; | ||||||||
165 | void dump() const; | ||||||||
166 | |||||||||
167 | friend ::llvm::hash_code hash_value(Type arg); | ||||||||
168 | |||||||||
169 | /// Methods for supporting PointerLikeTypeTraits. | ||||||||
170 | const void *getAsOpaquePointer() const { | ||||||||
171 | return static_cast<const void *>(impl); | ||||||||
172 | } | ||||||||
173 | static Type getFromOpaquePointer(const void *pointer) { | ||||||||
174 | return Type(reinterpret_cast<ImplType *>(const_cast<void *>(pointer))); | ||||||||
175 | } | ||||||||
176 | |||||||||
177 | /// Returns true if the type was registered with a particular trait. | ||||||||
178 | template <template <typename T> class Trait> | ||||||||
179 | bool hasTrait() { | ||||||||
180 | return getAbstractType().hasTrait<Trait>(); | ||||||||
181 | } | ||||||||
182 | |||||||||
183 | /// Return the abstract type descriptor for this type. | ||||||||
184 | const AbstractTy &getAbstractType() { return impl->getAbstractType(); } | ||||||||
185 | |||||||||
186 | /// Return the Type implementation. | ||||||||
187 | ImplType *getImpl() const { return impl; } | ||||||||
188 | |||||||||
189 | protected: | ||||||||
190 | ImplType *impl{nullptr}; | ||||||||
191 | }; | ||||||||
192 | |||||||||
193 | inline raw_ostream &operator<<(raw_ostream &os, Type type) { | ||||||||
194 | type.print(os); | ||||||||
195 | return os; | ||||||||
196 | } | ||||||||
197 | |||||||||
198 | //===----------------------------------------------------------------------===// | ||||||||
199 | // TypeTraitBase | ||||||||
200 | //===----------------------------------------------------------------------===// | ||||||||
201 | |||||||||
202 | namespace TypeTrait { | ||||||||
203 | /// This class represents the base of a type trait. | ||||||||
204 | template <typename ConcreteType, template <typename> class TraitType> | ||||||||
205 | using TraitBase = detail::StorageUserTraitBase<ConcreteType, TraitType>; | ||||||||
206 | } // namespace TypeTrait | ||||||||
207 | |||||||||
208 | //===----------------------------------------------------------------------===// | ||||||||
209 | // TypeInterface | ||||||||
210 | //===----------------------------------------------------------------------===// | ||||||||
211 | |||||||||
212 | /// This class represents the base of a type interface. See the definition of | ||||||||
213 | /// `detail::Interface` for requirements on the `Traits` type. | ||||||||
214 | template <typename ConcreteType, typename Traits> | ||||||||
215 | class TypeInterface : public detail::Interface<ConcreteType, Type, Traits, Type, | ||||||||
216 | TypeTrait::TraitBase> { | ||||||||
217 | public: | ||||||||
218 | using Base = TypeInterface<ConcreteType, Traits>; | ||||||||
219 | using InterfaceBase = | ||||||||
220 | detail::Interface<ConcreteType, Type, Traits, Type, TypeTrait::TraitBase>; | ||||||||
221 | using InterfaceBase::InterfaceBase; | ||||||||
222 | |||||||||
223 | private: | ||||||||
224 | /// Returns the impl interface instance for the given type. | ||||||||
225 | static typename InterfaceBase::Concept *getInterfaceFor(Type type) { | ||||||||
226 | return type.getAbstractType().getInterface<ConcreteType>(); | ||||||||
227 | } | ||||||||
228 | |||||||||
229 | /// Allow access to 'getInterfaceFor'. | ||||||||
230 | friend InterfaceBase; | ||||||||
231 | }; | ||||||||
232 | |||||||||
233 | //===----------------------------------------------------------------------===// | ||||||||
234 | // Core TypeTrait | ||||||||
235 | //===----------------------------------------------------------------------===// | ||||||||
236 | |||||||||
237 | /// This trait is used to determine if a type is mutable or not. It is attached | ||||||||
238 | /// on a type if the corresponding ImplType defines a `mutate` function with | ||||||||
239 | /// a proper signature. | ||||||||
240 | namespace TypeTrait { | ||||||||
241 | template <typename ConcreteType> | ||||||||
242 | using IsMutable = detail::StorageUserTrait::IsMutable<ConcreteType>; | ||||||||
243 | } // namespace TypeTrait | ||||||||
244 | |||||||||
245 | //===----------------------------------------------------------------------===// | ||||||||
246 | // Type Utils | ||||||||
247 | //===----------------------------------------------------------------------===// | ||||||||
248 | |||||||||
249 | // Make Type hashable. | ||||||||
250 | inline ::llvm::hash_code hash_value(Type arg) { | ||||||||
251 | return DenseMapInfo<const Type::ImplType *>::getHashValue(arg.impl); | ||||||||
252 | } | ||||||||
253 | |||||||||
254 | template <typename... Tys> | ||||||||
255 | bool Type::isa() const { | ||||||||
256 | return llvm::isa<Tys...>(*this); | ||||||||
257 | } | ||||||||
258 | |||||||||
259 | template <typename... Tys> | ||||||||
260 | bool Type::isa_and_nonnull() const { | ||||||||
261 | return llvm::isa_and_present<Tys...>(*this); | ||||||||
262 | } | ||||||||
263 | |||||||||
264 | template <typename U> | ||||||||
265 | U Type::dyn_cast() const { | ||||||||
266 | return llvm::dyn_cast<U>(*this); | ||||||||
267 | } | ||||||||
268 | |||||||||
269 | template <typename U> | ||||||||
270 | U Type::dyn_cast_or_null() const { | ||||||||
271 | return llvm::dyn_cast_or_null<U>(*this); | ||||||||
272 | } | ||||||||
273 | |||||||||
274 | template <typename U> | ||||||||
275 | U Type::cast() const { | ||||||||
276 | return llvm::cast<U>(*this); | ||||||||
277 | } | ||||||||
278 | |||||||||
279 | } // namespace mlir | ||||||||
280 | |||||||||
281 | namespace llvm { | ||||||||
282 | |||||||||
283 | // Type hash just like pointers. | ||||||||
284 | template <> | ||||||||
285 | struct DenseMapInfo<mlir::Type> { | ||||||||
286 | static mlir::Type getEmptyKey() { | ||||||||
287 | auto *pointer = llvm::DenseMapInfo<void *>::getEmptyKey(); | ||||||||
288 | return mlir::Type(static_cast<mlir::Type::ImplType *>(pointer)); | ||||||||
289 | } | ||||||||
290 | static mlir::Type getTombstoneKey() { | ||||||||
291 | auto *pointer = llvm::DenseMapInfo<void *>::getTombstoneKey(); | ||||||||
292 | return mlir::Type(static_cast<mlir::Type::ImplType *>(pointer)); | ||||||||
293 | } | ||||||||
294 | static unsigned getHashValue(mlir::Type val) { return mlir::hash_value(val); } | ||||||||
295 | static bool isEqual(mlir::Type LHS, mlir::Type RHS) { return LHS == RHS; } | ||||||||
296 | }; | ||||||||
297 | template <typename T> | ||||||||
298 | struct DenseMapInfo<T, std::enable_if_t<std::is_base_of<mlir::Type, T>::value && | ||||||||
299 | !mlir::detail::IsInterface<T>::value>> | ||||||||
300 | : public DenseMapInfo<mlir::Type> { | ||||||||
301 | static T getEmptyKey() { | ||||||||
302 | const void *pointer = llvm::DenseMapInfo<const void *>::getEmptyKey(); | ||||||||
303 | return T::getFromOpaquePointer(pointer); | ||||||||
304 | } | ||||||||
305 | static T getTombstoneKey() { | ||||||||
306 | const void *pointer = llvm::DenseMapInfo<const void *>::getTombstoneKey(); | ||||||||
307 | return T::getFromOpaquePointer(pointer); | ||||||||
308 | } | ||||||||
309 | }; | ||||||||
310 | |||||||||
311 | /// We align TypeStorage by 8, so allow LLVM to steal the low bits. | ||||||||
312 | template <> | ||||||||
313 | struct PointerLikeTypeTraits<mlir::Type> { | ||||||||
314 | public: | ||||||||
315 | static inline void *getAsVoidPointer(mlir::Type I) { | ||||||||
316 | return const_cast<void *>(I.getAsOpaquePointer()); | ||||||||
317 | } | ||||||||
318 | static inline mlir::Type getFromVoidPointer(void *P) { | ||||||||
319 | return mlir::Type::getFromOpaquePointer(P); | ||||||||
320 | } | ||||||||
321 | static constexpr int NumLowBitsAvailable = 3; | ||||||||
322 | }; | ||||||||
323 | |||||||||
324 | /// Add support for llvm style casts. | ||||||||
325 | /// We provide a cast between To and From if From is mlir::Type or derives from | ||||||||
326 | /// it | ||||||||
327 | template <typename To, typename From> | ||||||||
328 | struct CastInfo< | ||||||||
329 | To, From, | ||||||||
330 | std::enable_if_t<std::is_same_v<mlir::Type, std::remove_const_t<From>> || | ||||||||
331 | std::is_base_of_v<mlir::Type, From>>> | ||||||||
332 | : NullableValueCastFailed<To>, | ||||||||
333 | DefaultDoCastIfPossible<To, From, CastInfo<To, From>> { | ||||||||
334 | /// Arguments are taken as mlir::Type here and not as From. | ||||||||
335 | /// Because when casting from an intermediate type of the hierarchy to one of | ||||||||
336 | /// its children, the val.getTypeID() inside T::classof will use the static | ||||||||
337 | /// getTypeID of the parent instead of the non-static Type::getTypeID return | ||||||||
338 | /// the dynamic ID. so T::classof would end up comparing the static TypeID of | ||||||||
339 | /// The children to the static TypeID of its parent making it impossible to | ||||||||
340 | /// downcast from the parent to the child | ||||||||
341 | static inline bool isPossible(mlir::Type ty) { | ||||||||
342 | /// Return a constant true instead of a dynamic true when casting to self or | ||||||||
343 | /// up the hierarchy | ||||||||
344 | return std::is_same_v<To, std::remove_const_t<From>>
| ||||||||
345 | std::is_base_of_v<To, From>
| ||||||||
346 | } | ||||||||
347 | static inline To doCast(mlir::Type ty) { return To(ty.getImpl()); } | ||||||||
348 | }; | ||||||||
349 | |||||||||
350 | } // namespace llvm | ||||||||
351 | |||||||||
352 | #endif // MLIR_IR_TYPES_H |
1 | //===- llvm/Support/Casting.h - Allow flexible, checked, casts --*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file defines the isa<X>(), cast<X>(), dyn_cast<X>(), |
10 | // cast_if_present<X>(), and dyn_cast_if_present<X>() templates. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #ifndef LLVM_SUPPORT_CASTING_H |
15 | #define LLVM_SUPPORT_CASTING_H |
16 | |
17 | #include "llvm/ADT/Optional.h" |
18 | #include "llvm/Support/Compiler.h" |
19 | #include "llvm/Support/type_traits.h" |
20 | #include <cassert> |
21 | #include <memory> |
22 | #include <type_traits> |
23 | |
24 | namespace llvm { |
25 | |
26 | //===----------------------------------------------------------------------===// |
27 | // simplify_type |
28 | //===----------------------------------------------------------------------===// |
29 | |
30 | /// Define a template that can be specialized by smart pointers to reflect the |
31 | /// fact that they are automatically dereferenced, and are not involved with the |
32 | /// template selection process... the default implementation is a noop. |
33 | // TODO: rename this and/or replace it with other cast traits. |
34 | template <typename From> struct simplify_type { |
35 | using SimpleType = From; // The real type this represents... |
36 | |
37 | // An accessor to get the real value... |
38 | static SimpleType &getSimplifiedValue(From &Val) { return Val; } |
39 | }; |
40 | |
41 | template <typename From> struct simplify_type<const From> { |
42 | using NonConstSimpleType = typename simplify_type<From>::SimpleType; |
43 | using SimpleType = typename add_const_past_pointer<NonConstSimpleType>::type; |
44 | using RetType = |
45 | typename add_lvalue_reference_if_not_pointer<SimpleType>::type; |
46 | |
47 | static RetType getSimplifiedValue(const From &Val) { |
48 | return simplify_type<From>::getSimplifiedValue(const_cast<From &>(Val)); |
49 | } |
50 | }; |
51 | |
52 | // TODO: add this namespace once everyone is switched to using the new |
53 | // interface. |
54 | // namespace detail { |
55 | |
56 | //===----------------------------------------------------------------------===// |
57 | // isa_impl |
58 | //===----------------------------------------------------------------------===// |
59 | |
60 | // The core of the implementation of isa<X> is here; To and From should be |
61 | // the names of classes. This template can be specialized to customize the |
62 | // implementation of isa<> without rewriting it from scratch. |
63 | template <typename To, typename From, typename Enabler = void> struct isa_impl { |
64 | static inline bool doit(const From &Val) { return To::classof(&Val); } |
65 | }; |
66 | |
67 | // Always allow upcasts, and perform no dynamic check for them. |
68 | template <typename To, typename From> |
69 | struct isa_impl<To, From, std::enable_if_t<std::is_base_of<To, From>::value>> { |
70 | static inline bool doit(const From &) { return true; } |
71 | }; |
72 | |
73 | template <typename To, typename From> struct isa_impl_cl { |
74 | static inline bool doit(const From &Val) { |
75 | return isa_impl<To, From>::doit(Val); |
76 | } |
77 | }; |
78 | |
79 | template <typename To, typename From> struct isa_impl_cl<To, const From> { |
80 | static inline bool doit(const From &Val) { |
81 | return isa_impl<To, From>::doit(Val); |
82 | } |
83 | }; |
84 | |
85 | template <typename To, typename From> |
86 | struct isa_impl_cl<To, const std::unique_ptr<From>> { |
87 | static inline bool doit(const std::unique_ptr<From> &Val) { |
88 | assert(Val && "isa<> used on a null pointer")(static_cast <bool> (Val && "isa<> used on a null pointer" ) ? void (0) : __assert_fail ("Val && \"isa<> used on a null pointer\"" , "llvm/include/llvm/Support/Casting.h", 88, __extension__ __PRETTY_FUNCTION__ )); |
89 | return isa_impl_cl<To, From>::doit(*Val); |
90 | } |
91 | }; |
92 | |
93 | template <typename To, typename From> struct isa_impl_cl<To, From *> { |
94 | static inline bool doit(const From *Val) { |
95 | assert(Val && "isa<> used on a null pointer")(static_cast <bool> (Val && "isa<> used on a null pointer" ) ? void (0) : __assert_fail ("Val && \"isa<> used on a null pointer\"" , "llvm/include/llvm/Support/Casting.h", 95, __extension__ __PRETTY_FUNCTION__ )); |
96 | return isa_impl<To, From>::doit(*Val); |
97 | } |
98 | }; |
99 | |
100 | template <typename To, typename From> struct isa_impl_cl<To, From *const> { |
101 | static inline bool doit(const From *Val) { |
102 | assert(Val && "isa<> used on a null pointer")(static_cast <bool> (Val && "isa<> used on a null pointer" ) ? void (0) : __assert_fail ("Val && \"isa<> used on a null pointer\"" , "llvm/include/llvm/Support/Casting.h", 102, __extension__ __PRETTY_FUNCTION__ )); |
103 | return isa_impl<To, From>::doit(*Val); |
104 | } |
105 | }; |
106 | |
107 | template <typename To, typename From> struct isa_impl_cl<To, const From *> { |
108 | static inline bool doit(const From *Val) { |
109 | assert(Val && "isa<> used on a null pointer")(static_cast <bool> (Val && "isa<> used on a null pointer" ) ? void (0) : __assert_fail ("Val && \"isa<> used on a null pointer\"" , "llvm/include/llvm/Support/Casting.h", 109, __extension__ __PRETTY_FUNCTION__ )); |
110 | return isa_impl<To, From>::doit(*Val); |
111 | } |
112 | }; |
113 | |
114 | template <typename To, typename From> |
115 | struct isa_impl_cl<To, const From *const> { |
116 | static inline bool doit(const From *Val) { |
117 | assert(Val && "isa<> used on a null pointer")(static_cast <bool> (Val && "isa<> used on a null pointer" ) ? void (0) : __assert_fail ("Val && \"isa<> used on a null pointer\"" , "llvm/include/llvm/Support/Casting.h", 117, __extension__ __PRETTY_FUNCTION__ )); |
118 | return isa_impl<To, From>::doit(*Val); |
119 | } |
120 | }; |
121 | |
122 | template <typename To, typename From, typename SimpleFrom> |
123 | struct isa_impl_wrap { |
124 | // When From != SimplifiedType, we can simplify the type some more by using |
125 | // the simplify_type template. |
126 | static bool doit(const From &Val) { |
127 | return isa_impl_wrap<To, SimpleFrom, |
128 | typename simplify_type<SimpleFrom>::SimpleType>:: |
129 | doit(simplify_type<const From>::getSimplifiedValue(Val)); |
130 | } |
131 | }; |
132 | |
133 | template <typename To, typename FromTy> |
134 | struct isa_impl_wrap<To, FromTy, FromTy> { |
135 | // When From == SimpleType, we are as simple as we are going to get. |
136 | static bool doit(const FromTy &Val) { |
137 | return isa_impl_cl<To, FromTy>::doit(Val); |
138 | } |
139 | }; |
140 | |
141 | //===----------------------------------------------------------------------===// |
142 | // cast_retty + cast_retty_impl |
143 | //===----------------------------------------------------------------------===// |
144 | |
145 | template <class To, class From> struct cast_retty; |
146 | |
147 | // Calculate what type the 'cast' function should return, based on a requested |
148 | // type of To and a source type of From. |
149 | template <class To, class From> struct cast_retty_impl { |
150 | using ret_type = To &; // Normal case, return Ty& |
151 | }; |
152 | template <class To, class From> struct cast_retty_impl<To, const From> { |
153 | using ret_type = const To &; // Normal case, return Ty& |
154 | }; |
155 | |
156 | template <class To, class From> struct cast_retty_impl<To, From *> { |
157 | using ret_type = To *; // Pointer arg case, return Ty* |
158 | }; |
159 | |
160 | template <class To, class From> struct cast_retty_impl<To, const From *> { |
161 | using ret_type = const To *; // Constant pointer arg case, return const Ty* |
162 | }; |
163 | |
164 | template <class To, class From> struct cast_retty_impl<To, const From *const> { |
165 | using ret_type = const To *; // Constant pointer arg case, return const Ty* |
166 | }; |
167 | |
168 | template <class To, class From> |
169 | struct cast_retty_impl<To, std::unique_ptr<From>> { |
170 | private: |
171 | using PointerType = typename cast_retty_impl<To, From *>::ret_type; |
172 | using ResultType = std::remove_pointer_t<PointerType>; |
173 | |
174 | public: |
175 | using ret_type = std::unique_ptr<ResultType>; |
176 | }; |
177 | |
178 | template <class To, class From, class SimpleFrom> struct cast_retty_wrap { |
179 | // When the simplified type and the from type are not the same, use the type |
180 | // simplifier to reduce the type, then reuse cast_retty_impl to get the |
181 | // resultant type. |
182 | using ret_type = typename cast_retty<To, SimpleFrom>::ret_type; |
183 | }; |
184 | |
185 | template <class To, class FromTy> struct cast_retty_wrap<To, FromTy, FromTy> { |
186 | // When the simplified type is equal to the from type, use it directly. |
187 | using ret_type = typename cast_retty_impl<To, FromTy>::ret_type; |
188 | }; |
189 | |
190 | template <class To, class From> struct cast_retty { |
191 | using ret_type = typename cast_retty_wrap< |
192 | To, From, typename simplify_type<From>::SimpleType>::ret_type; |
193 | }; |
194 | |
195 | //===----------------------------------------------------------------------===// |
196 | // cast_convert_val |
197 | //===----------------------------------------------------------------------===// |
198 | |
199 | // Ensure the non-simple values are converted using the simplify_type template |
200 | // that may be specialized by smart pointers... |
201 | // |
202 | template <class To, class From, class SimpleFrom> struct cast_convert_val { |
203 | // This is not a simple type, use the template to simplify it... |
204 | static typename cast_retty<To, From>::ret_type doit(const From &Val) { |
205 | return cast_convert_val<To, SimpleFrom, |
206 | typename simplify_type<SimpleFrom>::SimpleType>:: |
207 | doit(simplify_type<From>::getSimplifiedValue(const_cast<From &>(Val))); |
208 | } |
209 | }; |
210 | |
211 | template <class To, class FromTy> struct cast_convert_val<To, FromTy, FromTy> { |
212 | // If it's a reference, switch to a pointer to do the cast and then deref it. |
213 | static typename cast_retty<To, FromTy>::ret_type doit(const FromTy &Val) { |
214 | return *(std::remove_reference_t<typename cast_retty<To, FromTy>::ret_type> |
215 | *)&const_cast<FromTy &>(Val); |
216 | } |
217 | }; |
218 | |
219 | template <class To, class FromTy> |
220 | struct cast_convert_val<To, FromTy *, FromTy *> { |
221 | // If it's a pointer, we can use c-style casting directly. |
222 | static typename cast_retty<To, FromTy *>::ret_type doit(const FromTy *Val) { |
223 | return (typename cast_retty<To, FromTy *>::ret_type) const_cast<FromTy *>( |
224 | Val); |
225 | } |
226 | }; |
227 | |
228 | //===----------------------------------------------------------------------===// |
229 | // is_simple_type |
230 | //===----------------------------------------------------------------------===// |
231 | |
232 | template <class X> struct is_simple_type { |
233 | static const bool value = |
234 | std::is_same<X, typename simplify_type<X>::SimpleType>::value; |
235 | }; |
236 | |
237 | // } // namespace detail |
238 | |
239 | //===----------------------------------------------------------------------===// |
240 | // CastIsPossible |
241 | //===----------------------------------------------------------------------===// |
242 | |
243 | /// This struct provides a way to check if a given cast is possible. It provides |
244 | /// a static function called isPossible that is used to check if a cast can be |
245 | /// performed. It should be overridden like this: |
246 | /// |
247 | /// template<> struct CastIsPossible<foo, bar> { |
248 | /// static inline bool isPossible(const bar &b) { |
249 | /// return bar.isFoo(); |
250 | /// } |
251 | /// }; |
252 | template <typename To, typename From, typename Enable = void> |
253 | struct CastIsPossible { |
254 | static inline bool isPossible(const From &f) { |
255 | return isa_impl_wrap< |
256 | To, const From, |
257 | typename simplify_type<const From>::SimpleType>::doit(f); |
258 | } |
259 | }; |
260 | |
261 | // Needed for optional unwrapping. This could be implemented with isa_impl, but |
262 | // we want to implement things in the new method and move old implementations |
263 | // over. In fact, some of the isa_impl templates should be moved over to |
264 | // CastIsPossible. |
265 | template <typename To, typename From> |
266 | struct CastIsPossible<To, Optional<From>> { |
267 | static inline bool isPossible(const Optional<From> &f) { |
268 | assert(f && "CastIsPossible::isPossible called on a nullopt!")(static_cast <bool> (f && "CastIsPossible::isPossible called on a nullopt!" ) ? void (0) : __assert_fail ("f && \"CastIsPossible::isPossible called on a nullopt!\"" , "llvm/include/llvm/Support/Casting.h", 268, __extension__ __PRETTY_FUNCTION__ )); |
269 | return isa_impl_wrap< |
270 | To, const From, |
271 | typename simplify_type<const From>::SimpleType>::doit(*f); |
272 | } |
273 | }; |
274 | |
275 | /// Upcasting (from derived to base) and casting from a type to itself should |
276 | /// always be possible. |
277 | template <typename To, typename From> |
278 | struct CastIsPossible<To, From, |
279 | std::enable_if_t<std::is_base_of<To, From>::value>> { |
280 | static inline bool isPossible(const From &f) { return true; } |
281 | }; |
282 | |
283 | //===----------------------------------------------------------------------===// |
284 | // Cast traits |
285 | //===----------------------------------------------------------------------===// |
286 | |
287 | /// All of these cast traits are meant to be implementations for useful casts |
288 | /// that users may want to use that are outside the standard behavior. An |
289 | /// example of how to use a special cast called `CastTrait` is: |
290 | /// |
291 | /// template<> struct CastInfo<foo, bar> : public CastTrait<foo, bar> {}; |
292 | /// |
293 | /// Essentially, if your use case falls directly into one of the use cases |
294 | /// supported by a given cast trait, simply inherit your special CastInfo |
295 | /// directly from one of these to avoid having to reimplement the boilerplate |
296 | /// `isPossible/castFailed/doCast/doCastIfPossible`. A cast trait can also |
297 | /// provide a subset of those functions. |
298 | |
299 | /// This cast trait just provides castFailed for the specified `To` type to make |
300 | /// CastInfo specializations more declarative. In order to use this, the target |
301 | /// result type must be `To` and `To` must be constructible from `nullptr`. |
302 | template <typename To> struct NullableValueCastFailed { |
303 | static To castFailed() { return To(nullptr); } |
304 | }; |
305 | |
306 | /// This cast trait just provides the default implementation of doCastIfPossible |
307 | /// to make CastInfo specializations more declarative. The `Derived` template |
308 | /// parameter *must* be provided for forwarding castFailed and doCast. |
309 | template <typename To, typename From, typename Derived> |
310 | struct DefaultDoCastIfPossible { |
311 | static To doCastIfPossible(From f) { |
312 | if (!Derived::isPossible(f)) |
313 | return Derived::castFailed(); |
314 | return Derived::doCast(f); |
315 | } |
316 | }; |
317 | |
318 | namespace detail { |
319 | /// A helper to derive the type to use with `Self` for cast traits, when the |
320 | /// provided CRTP derived type is allowed to be void. |
321 | template <typename OptionalDerived, typename Default> |
322 | using SelfType = std::conditional_t<std::is_same<OptionalDerived, void>::value, |
323 | Default, OptionalDerived>; |
324 | } // namespace detail |
325 | |
326 | /// This cast trait provides casting for the specific case of casting to a |
327 | /// value-typed object from a pointer-typed object. Note that `To` must be |
328 | /// nullable/constructible from a pointer to `From` to use this cast. |
329 | template <typename To, typename From, typename Derived = void> |
330 | struct ValueFromPointerCast |
331 | : public CastIsPossible<To, From *>, |
332 | public NullableValueCastFailed<To>, |
333 | public DefaultDoCastIfPossible< |
334 | To, From *, |
335 | detail::SelfType<Derived, ValueFromPointerCast<To, From>>> { |
336 | static inline To doCast(From *f) { return To(f); } |
337 | }; |
338 | |
339 | /// This cast trait provides std::unique_ptr casting. It has the semantics of |
340 | /// moving the contents of the input unique_ptr into the output unique_ptr |
341 | /// during the cast. It's also a good example of how to implement a move-only |
342 | /// cast. |
343 | template <typename To, typename From, typename Derived = void> |
344 | struct UniquePtrCast : public CastIsPossible<To, From *> { |
345 | using Self = detail::SelfType<Derived, UniquePtrCast<To, From>>; |
346 | using CastResultType = std::unique_ptr< |
347 | std::remove_reference_t<typename cast_retty<To, From>::ret_type>>; |
348 | |
349 | static inline CastResultType doCast(std::unique_ptr<From> &&f) { |
350 | return CastResultType((typename CastResultType::element_type *)f.release()); |
351 | } |
352 | |
353 | static inline CastResultType castFailed() { return CastResultType(nullptr); } |
354 | |
355 | static inline CastResultType doCastIfPossible(std::unique_ptr<From> &&f) { |
356 | if (!Self::isPossible(f)) |
357 | return castFailed(); |
358 | return doCast(f); |
359 | } |
360 | }; |
361 | |
362 | /// This cast trait provides Optional<T> casting. This means that if you have a |
363 | /// value type, you can cast it to another value type and have dyn_cast return |
364 | /// an Optional<T>. |
365 | template <typename To, typename From, typename Derived = void> |
366 | struct OptionalValueCast |
367 | : public CastIsPossible<To, From>, |
368 | public DefaultDoCastIfPossible< |
369 | Optional<To>, From, |
370 | detail::SelfType<Derived, OptionalValueCast<To, From>>> { |
371 | static inline Optional<To> castFailed() { return Optional<To>{}; } |
372 | |
373 | static inline Optional<To> doCast(const From &f) { return To(f); } |
374 | }; |
375 | |
376 | /// Provides a cast trait that strips `const` from types to make it easier to |
377 | /// implement a const-version of a non-const cast. It just removes boilerplate |
378 | /// and reduces the amount of code you as the user need to implement. You can |
379 | /// use it like this: |
380 | /// |
381 | /// template<> struct CastInfo<foo, bar> { |
382 | /// ...verbose implementation... |
383 | /// }; |
384 | /// |
385 | /// template<> struct CastInfo<foo, const bar> : public |
386 | /// ConstStrippingForwardingCast<foo, const bar, CastInfo<foo, bar>> {}; |
387 | /// |
388 | template <typename To, typename From, typename ForwardTo> |
389 | struct ConstStrippingForwardingCast { |
390 | // Remove the pointer if it exists, then we can get rid of consts/volatiles. |
391 | using DecayedFrom = std::remove_cv_t<std::remove_pointer_t<From>>; |
392 | // Now if it's a pointer, add it back. Otherwise, we want a ref. |
393 | using NonConstFrom = std::conditional_t<std::is_pointer<From>::value, |
394 | DecayedFrom *, DecayedFrom &>; |
395 | |
396 | static inline bool isPossible(const From &f) { |
397 | return ForwardTo::isPossible(const_cast<NonConstFrom>(f)); |
398 | } |
399 | |
400 | static inline decltype(auto) castFailed() { return ForwardTo::castFailed(); } |
401 | |
402 | static inline decltype(auto) doCast(const From &f) { |
403 | return ForwardTo::doCast(const_cast<NonConstFrom>(f)); |
404 | } |
405 | |
406 | static inline decltype(auto) doCastIfPossible(const From &f) { |
407 | return ForwardTo::doCastIfPossible(const_cast<NonConstFrom>(f)); |
408 | } |
409 | }; |
410 | |
411 | /// Provides a cast trait that uses a defined pointer to pointer cast as a base |
412 | /// for reference-to-reference casts. Note that it does not provide castFailed |
413 | /// and doCastIfPossible because a pointer-to-pointer cast would likely just |
414 | /// return `nullptr` which could cause nullptr dereference. You can use it like |
415 | /// this: |
416 | /// |
417 | /// template <> struct CastInfo<foo, bar *> { ... verbose implementation... }; |
418 | /// |
419 | /// template <> |
420 | /// struct CastInfo<foo, bar> |
421 | /// : public ForwardToPointerCast<foo, bar, CastInfo<foo, bar *>> {}; |
422 | /// |
423 | template <typename To, typename From, typename ForwardTo> |
424 | struct ForwardToPointerCast { |
425 | static inline bool isPossible(const From &f) { |
426 | return ForwardTo::isPossible(&f); |
427 | } |
428 | |
429 | static inline decltype(auto) doCast(const From &f) { |
430 | return *ForwardTo::doCast(&f); |
431 | } |
432 | }; |
433 | |
434 | //===----------------------------------------------------------------------===// |
435 | // CastInfo |
436 | //===----------------------------------------------------------------------===// |
437 | |
438 | /// This struct provides a method for customizing the way a cast is performed. |
439 | /// It inherits from CastIsPossible, to support the case of declaring many |
440 | /// CastIsPossible specializations without having to specialize the full |
441 | /// CastInfo. |
442 | /// |
443 | /// In order to specialize different behaviors, specify different functions in |
444 | /// your CastInfo specialization. |
445 | /// For isa<> customization, provide: |
446 | /// |
447 | /// `static bool isPossible(const From &f)` |
448 | /// |
449 | /// For cast<> customization, provide: |
450 | /// |
451 | /// `static To doCast(const From &f)` |
452 | /// |
453 | /// For dyn_cast<> and the *_if_present<> variants' customization, provide: |
454 | /// |
455 | /// `static To castFailed()` and `static To doCastIfPossible(const From &f)` |
456 | /// |
457 | /// Your specialization might look something like this: |
458 | /// |
459 | /// template<> struct CastInfo<foo, bar> : public CastIsPossible<foo, bar> { |
460 | /// static inline foo doCast(const bar &b) { |
461 | /// return foo(const_cast<bar &>(b)); |
462 | /// } |
463 | /// static inline foo castFailed() { return foo(); } |
464 | /// static inline foo doCastIfPossible(const bar &b) { |
465 | /// if (!CastInfo<foo, bar>::isPossible(b)) |
466 | /// return castFailed(); |
467 | /// return doCast(b); |
468 | /// } |
469 | /// }; |
470 | |
471 | // The default implementations of CastInfo don't use cast traits for now because |
472 | // we need to specify types all over the place due to the current expected |
473 | // casting behavior and the way cast_retty works. New use cases can and should |
474 | // take advantage of the cast traits whenever possible! |
475 | |
476 | template <typename To, typename From, typename Enable = void> |
477 | struct CastInfo : public CastIsPossible<To, From> { |
478 | using Self = CastInfo<To, From, Enable>; |
479 | |
480 | using CastReturnType = typename cast_retty<To, From>::ret_type; |
481 | |
482 | static inline CastReturnType doCast(const From &f) { |
483 | return cast_convert_val< |
484 | To, From, |
485 | typename simplify_type<From>::SimpleType>::doit(const_cast<From &>(f)); |
486 | } |
487 | |
488 | // This assumes that you can construct the cast return type from `nullptr`. |
489 | // This is largely to support legacy use cases - if you don't want this |
490 | // behavior you should specialize CastInfo for your use case. |
491 | static inline CastReturnType castFailed() { return CastReturnType(nullptr); } |
492 | |
493 | static inline CastReturnType doCastIfPossible(const From &f) { |
494 | if (!Self::isPossible(f)) |
495 | return castFailed(); |
496 | return doCast(f); |
497 | } |
498 | }; |
499 | |
500 | /// This struct provides an overload for CastInfo where From has simplify_type |
501 | /// defined. This simply forwards to the appropriate CastInfo with the |
502 | /// simplified type/value, so you don't have to implement both. |
503 | template <typename To, typename From> |
504 | struct CastInfo<To, From, std::enable_if_t<!is_simple_type<From>::value>> { |
505 | using Self = CastInfo<To, From>; |
506 | using SimpleFrom = typename simplify_type<From>::SimpleType; |
507 | using SimplifiedSelf = CastInfo<To, SimpleFrom>; |
508 | |
509 | static inline bool isPossible(From &f) { |
510 | return SimplifiedSelf::isPossible( |
511 | simplify_type<From>::getSimplifiedValue(f)); |
512 | } |
513 | |
514 | static inline decltype(auto) doCast(From &f) { |
515 | return SimplifiedSelf::doCast(simplify_type<From>::getSimplifiedValue(f)); |
516 | } |
517 | |
518 | static inline decltype(auto) castFailed() { |
519 | return SimplifiedSelf::castFailed(); |
520 | } |
521 | |
522 | static inline decltype(auto) doCastIfPossible(From &f) { |
523 | return SimplifiedSelf::doCastIfPossible( |
524 | simplify_type<From>::getSimplifiedValue(f)); |
525 | } |
526 | }; |
527 | |
528 | //===----------------------------------------------------------------------===// |
529 | // Pre-specialized CastInfo |
530 | //===----------------------------------------------------------------------===// |
531 | |
532 | /// Provide a CastInfo specialized for std::unique_ptr. |
533 | template <typename To, typename From> |
534 | struct CastInfo<To, std::unique_ptr<From>> : public UniquePtrCast<To, From> {}; |
535 | |
536 | /// Provide a CastInfo specialized for Optional<From>. It's assumed that if the |
537 | /// input is Optional<From> that the output can be Optional<To>. If that's not |
538 | /// the case, specialize CastInfo for your use case. |
539 | template <typename To, typename From> |
540 | struct CastInfo<To, Optional<From>> : public OptionalValueCast<To, From> {}; |
541 | |
542 | /// isa<X> - Return true if the parameter to the template is an instance of one |
543 | /// of the template type arguments. Used like this: |
544 | /// |
545 | /// if (isa<Type>(myVal)) { ... } |
546 | /// if (isa<Type0, Type1, Type2>(myVal)) { ... } |
547 | template <typename To, typename From> |
548 | [[nodiscard]] inline bool isa(const From &Val) { |
549 | return CastInfo<To, const From>::isPossible(Val); |
550 | } |
551 | |
552 | template <typename First, typename Second, typename... Rest, typename From> |
553 | [[nodiscard]] inline bool isa(const From &Val) { |
554 | return isa<First>(Val) || isa<Second, Rest...>(Val); |
555 | } |
556 | |
557 | /// cast<X> - Return the argument parameter cast to the specified type. This |
558 | /// casting operator asserts that the type is correct, so it does not return |
559 | /// null on failure. It does not allow a null argument (use cast_if_present for |
560 | /// that). It is typically used like this: |
561 | /// |
562 | /// cast<Instruction>(myVal)->getParent() |
563 | |
564 | template <typename To, typename From> |
565 | [[nodiscard]] inline decltype(auto) cast(const From &Val) { |
566 | assert(isa<To>(Val) && "cast<Ty>() argument of incompatible type!")(static_cast <bool> (isa<To>(Val) && "cast<Ty>() argument of incompatible type!" ) ? void (0) : __assert_fail ("isa<To>(Val) && \"cast<Ty>() argument of incompatible type!\"" , "llvm/include/llvm/Support/Casting.h", 566, __extension__ __PRETTY_FUNCTION__ )); |
567 | return CastInfo<To, const From>::doCast(Val); |
568 | } |
569 | |
570 | template <typename To, typename From> |
571 | [[nodiscard]] inline decltype(auto) cast(From &Val) { |
572 | assert(isa<To>(Val) && "cast<Ty>() argument of incompatible type!")(static_cast <bool> (isa<To>(Val) && "cast<Ty>() argument of incompatible type!" ) ? void (0) : __assert_fail ("isa<To>(Val) && \"cast<Ty>() argument of incompatible type!\"" , "llvm/include/llvm/Support/Casting.h", 572, __extension__ __PRETTY_FUNCTION__ )); |
573 | return CastInfo<To, From>::doCast(Val); |
574 | } |
575 | |
576 | template <typename To, typename From> |
577 | [[nodiscard]] inline decltype(auto) cast(From *Val) { |
578 | assert(isa<To>(Val) && "cast<Ty>() argument of incompatible type!")(static_cast <bool> (isa<To>(Val) && "cast<Ty>() argument of incompatible type!" ) ? void (0) : __assert_fail ("isa<To>(Val) && \"cast<Ty>() argument of incompatible type!\"" , "llvm/include/llvm/Support/Casting.h", 578, __extension__ __PRETTY_FUNCTION__ )); |
579 | return CastInfo<To, From *>::doCast(Val); |
580 | } |
581 | |
582 | template <typename To, typename From> |
583 | [[nodiscard]] inline decltype(auto) cast(std::unique_ptr<From> &&Val) { |
584 | assert(isa<To>(Val) && "cast<Ty>() argument of incompatible type!")(static_cast <bool> (isa<To>(Val) && "cast<Ty>() argument of incompatible type!" ) ? void (0) : __assert_fail ("isa<To>(Val) && \"cast<Ty>() argument of incompatible type!\"" , "llvm/include/llvm/Support/Casting.h", 584, __extension__ __PRETTY_FUNCTION__ )); |
585 | return CastInfo<To, std::unique_ptr<From>>::doCast(std::move(Val)); |
586 | } |
587 | |
588 | /// dyn_cast<X> - Return the argument parameter cast to the specified type. This |
589 | /// casting operator returns null if the argument is of the wrong type, so it |
590 | /// can be used to test for a type as well as cast if successful. The value |
591 | /// passed in must be present, if not, use dyn_cast_if_present. This should be |
592 | /// used in the context of an if statement like this: |
593 | /// |
594 | /// if (const Instruction *I = dyn_cast<Instruction>(myVal)) { ... } |
595 | |
596 | template <typename To, typename From> |
597 | [[nodiscard]] inline decltype(auto) dyn_cast(const From &Val) { |
598 | return CastInfo<To, const From>::doCastIfPossible(Val); |
599 | } |
600 | |
601 | template <typename To, typename From> |
602 | [[nodiscard]] inline decltype(auto) dyn_cast(From &Val) { |
603 | return CastInfo<To, From>::doCastIfPossible(Val); |
604 | } |
605 | |
606 | template <typename To, typename From> |
607 | [[nodiscard]] inline decltype(auto) dyn_cast(From *Val) { |
608 | return CastInfo<To, From *>::doCastIfPossible(Val); |
609 | } |
610 | |
611 | template <typename To, typename From> |
612 | [[nodiscard]] inline decltype(auto) dyn_cast(std::unique_ptr<From> &&Val) { |
613 | return CastInfo<To, std::unique_ptr<From>>::doCastIfPossible(std::move(Val)); |
614 | } |
615 | |
616 | //===----------------------------------------------------------------------===// |
617 | // ValueIsPresent |
618 | //===----------------------------------------------------------------------===// |
619 | |
620 | template <typename T> |
621 | constexpr bool IsNullable = std::is_pointer<T>::value || |
622 | std::is_constructible<T, std::nullptr_t>::value; |
623 | |
624 | /// ValueIsPresent provides a way to check if a value is, well, present. For |
625 | /// pointers, this is the equivalent of checking against nullptr, for |
626 | /// Optionals this is the equivalent of checking hasValue(). It also |
627 | /// provides a method for unwrapping a value (think dereferencing a |
628 | /// pointer). |
629 | |
630 | // Generic values can't *not* be present. |
631 | template <typename T, typename Enable = void> struct ValueIsPresent { |
632 | using UnwrappedType = T; |
633 | static inline bool isPresent(const T &t) { return true; } |
634 | static inline decltype(auto) unwrapValue(T &t) { return t; } |
635 | }; |
636 | |
637 | // Optional provides its own way to check if something is present. |
638 | template <typename T> struct ValueIsPresent<Optional<T>> { |
639 | using UnwrappedType = T; |
640 | static inline bool isPresent(const Optional<T> &t) { return t.has_value(); } |
641 | static inline decltype(auto) unwrapValue(Optional<T> &t) { return t.value(); } |
642 | }; |
643 | |
644 | // If something is "nullable" then we just compare it to nullptr to see if it |
645 | // exists. |
646 | template <typename T> |
647 | struct ValueIsPresent<T, std::enable_if_t<IsNullable<T>>> { |
648 | using UnwrappedType = T; |
649 | static inline bool isPresent(const T &t) { return t != nullptr; } |
650 | static inline decltype(auto) unwrapValue(T &t) { return t; } |
651 | }; |
652 | |
653 | namespace detail { |
654 | // Convenience function we can use to check if a value is present. Because of |
655 | // simplify_type, we have to call it on the simplified type for now. |
656 | template <typename T> inline bool isPresent(const T &t) { |
657 | return ValueIsPresent<typename simplify_type<T>::SimpleType>::isPresent( |
658 | simplify_type<T>::getSimplifiedValue(const_cast<T &>(t))); |
659 | } |
660 | |
661 | // Convenience function we can use to unwrap a value. |
662 | template <typename T> inline decltype(auto) unwrapValue(T &t) { |
663 | return ValueIsPresent<T>::unwrapValue(t); |
664 | } |
665 | } // namespace detail |
666 | |
667 | /// isa_and_present<X> - Functionally identical to isa, except that a null value |
668 | /// is accepted. |
669 | template <typename... X, class Y> |
670 | [[nodiscard]] inline bool isa_and_present(const Y &Val) { |
671 | if (!detail::isPresent(Val)) |
672 | return false; |
673 | return isa<X...>(Val); |
674 | } |
675 | |
676 | template <typename... X, class Y> |
677 | [[nodiscard]] inline bool isa_and_nonnull(const Y &Val) { |
678 | return isa_and_present<X...>(Val); |
679 | } |
680 | |
681 | /// cast_if_present<X> - Functionally identical to cast, except that a null |
682 | /// value is accepted. |
683 | template <class X, class Y> |
684 | [[nodiscard]] inline auto cast_if_present(const Y &Val) { |
685 | if (!detail::isPresent(Val)) |
686 | return CastInfo<X, const Y>::castFailed(); |
687 | assert(isa<X>(Val) && "cast_if_present<Ty>() argument of incompatible type!")(static_cast <bool> (isa<X>(Val) && "cast_if_present<Ty>() argument of incompatible type!" ) ? void (0) : __assert_fail ("isa<X>(Val) && \"cast_if_present<Ty>() argument of incompatible type!\"" , "llvm/include/llvm/Support/Casting.h", 687, __extension__ __PRETTY_FUNCTION__ )); |
688 | return cast<X>(detail::unwrapValue(Val)); |
689 | } |
690 | |
691 | template <class X, class Y> [[nodiscard]] inline auto cast_if_present(Y &Val) { |
692 | if (!detail::isPresent(Val)) |
693 | return CastInfo<X, Y>::castFailed(); |
694 | assert(isa<X>(Val) && "cast_if_present<Ty>() argument of incompatible type!")(static_cast <bool> (isa<X>(Val) && "cast_if_present<Ty>() argument of incompatible type!" ) ? void (0) : __assert_fail ("isa<X>(Val) && \"cast_if_present<Ty>() argument of incompatible type!\"" , "llvm/include/llvm/Support/Casting.h", 694, __extension__ __PRETTY_FUNCTION__ )); |
695 | return cast<X>(detail::unwrapValue(Val)); |
696 | } |
697 | |
698 | template <class X, class Y> [[nodiscard]] inline auto cast_if_present(Y *Val) { |
699 | if (!detail::isPresent(Val)) |
700 | return CastInfo<X, Y *>::castFailed(); |
701 | assert(isa<X>(Val) && "cast_if_present<Ty>() argument of incompatible type!")(static_cast <bool> (isa<X>(Val) && "cast_if_present<Ty>() argument of incompatible type!" ) ? void (0) : __assert_fail ("isa<X>(Val) && \"cast_if_present<Ty>() argument of incompatible type!\"" , "llvm/include/llvm/Support/Casting.h", 701, __extension__ __PRETTY_FUNCTION__ )); |
702 | return cast<X>(detail::unwrapValue(Val)); |
703 | } |
704 | |
705 | template <class X, class Y> |
706 | [[nodiscard]] inline auto cast_if_present(std::unique_ptr<Y> &&Val) { |
707 | if (!detail::isPresent(Val)) |
708 | return UniquePtrCast<X, Y>::castFailed(); |
709 | return UniquePtrCast<X, Y>::doCast(std::move(Val)); |
710 | } |
711 | |
712 | // Provide a forwarding from cast_or_null to cast_if_present for current |
713 | // users. This is deprecated and will be removed in a future patch, use |
714 | // cast_if_present instead. |
715 | template <class X, class Y> auto cast_or_null(const Y &Val) { |
716 | return cast_if_present<X>(Val); |
717 | } |
718 | |
719 | template <class X, class Y> auto cast_or_null(Y &Val) { |
720 | return cast_if_present<X>(Val); |
721 | } |
722 | |
723 | template <class X, class Y> auto cast_or_null(Y *Val) { |
724 | return cast_if_present<X>(Val); |
725 | } |
726 | |
727 | template <class X, class Y> auto cast_or_null(std::unique_ptr<Y> &&Val) { |
728 | return cast_if_present<X>(std::move(Val)); |
729 | } |
730 | |
731 | /// dyn_cast_if_present<X> - Functionally identical to dyn_cast, except that a |
732 | /// null (or none in the case of optionals) value is accepted. |
733 | template <class X, class Y> auto dyn_cast_if_present(const Y &Val) { |
734 | if (!detail::isPresent(Val)) |
735 | return CastInfo<X, const Y>::castFailed(); |
736 | return CastInfo<X, const Y>::doCastIfPossible(detail::unwrapValue(Val)); |
737 | } |
738 | |
739 | template <class X, class Y> auto dyn_cast_if_present(Y &Val) { |
740 | if (!detail::isPresent(Val)) |
741 | return CastInfo<X, Y>::castFailed(); |
742 | return CastInfo<X, Y>::doCastIfPossible(detail::unwrapValue(Val)); |
743 | } |
744 | |
745 | template <class X, class Y> auto dyn_cast_if_present(Y *Val) { |
746 | if (!detail::isPresent(Val)) |
747 | return CastInfo<X, Y *>::castFailed(); |
748 | return CastInfo<X, Y *>::doCastIfPossible(detail::unwrapValue(Val)); |
749 | } |
750 | |
751 | // Forwards to dyn_cast_if_present to avoid breaking current users. This is |
752 | // deprecated and will be removed in a future patch, use |
753 | // cast_if_present instead. |
754 | template <class X, class Y> auto dyn_cast_or_null(const Y &Val) { |
755 | return dyn_cast_if_present<X>(Val); |
756 | } |
757 | |
758 | template <class X, class Y> auto dyn_cast_or_null(Y &Val) { |
759 | return dyn_cast_if_present<X>(Val); |
760 | } |
761 | |
762 | template <class X, class Y> auto dyn_cast_or_null(Y *Val) { |
763 | return dyn_cast_if_present<X>(Val); |
764 | } |
765 | |
766 | /// unique_dyn_cast<X> - Given a unique_ptr<Y>, try to return a unique_ptr<X>, |
767 | /// taking ownership of the input pointer iff isa<X>(Val) is true. If the |
768 | /// cast is successful, From refers to nullptr on exit and the casted value |
769 | /// is returned. If the cast is unsuccessful, the function returns nullptr |
770 | /// and From is unchanged. |
771 | template <class X, class Y> |
772 | [[nodiscard]] inline typename CastInfo<X, std::unique_ptr<Y>>::CastResultType |
773 | unique_dyn_cast(std::unique_ptr<Y> &Val) { |
774 | if (!isa<X>(Val)) |
775 | return nullptr; |
776 | return cast<X>(std::move(Val)); |
777 | } |
778 | |
779 | template <class X, class Y> |
780 | [[nodiscard]] inline auto unique_dyn_cast(std::unique_ptr<Y> &&Val) { |
781 | return unique_dyn_cast<X, Y>(Val); |
782 | } |
783 | |
784 | // unique_dyn_cast_or_null<X> - Functionally identical to unique_dyn_cast, |
785 | // except that a null value is accepted. |
786 | template <class X, class Y> |
787 | [[nodiscard]] inline typename CastInfo<X, std::unique_ptr<Y>>::CastResultType |
788 | unique_dyn_cast_or_null(std::unique_ptr<Y> &Val) { |
789 | if (!Val) |
790 | return nullptr; |
791 | return unique_dyn_cast<X, Y>(Val); |
792 | } |
793 | |
794 | template <class X, class Y> |
795 | [[nodiscard]] inline auto unique_dyn_cast_or_null(std::unique_ptr<Y> &&Val) { |
796 | return unique_dyn_cast_or_null<X, Y>(Val); |
797 | } |
798 | |
799 | } // end namespace llvm |
800 | |
801 | #endif // LLVM_SUPPORT_CASTING_H |
1 | //===- StorageUniquerSupport.h - MLIR Storage Uniquer Utilities -*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file defines utility classes for interfacing with StorageUniquer. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #ifndef MLIR_IR_STORAGEUNIQUERSUPPORT_H |
14 | #define MLIR_IR_STORAGEUNIQUERSUPPORT_H |
15 | |
16 | #include "mlir/Support/InterfaceSupport.h" |
17 | #include "mlir/Support/LogicalResult.h" |
18 | #include "mlir/Support/StorageUniquer.h" |
19 | #include "mlir/Support/TypeID.h" |
20 | #include "llvm/ADT/FunctionExtras.h" |
21 | |
22 | namespace mlir { |
23 | class InFlightDiagnostic; |
24 | class Location; |
25 | class MLIRContext; |
26 | |
27 | namespace detail { |
28 | /// Utility method to generate a callback that can be used to generate a |
29 | /// diagnostic when checking the construction invariants of a storage object. |
30 | /// This is defined out-of-line to avoid the need to include Location.h. |
31 | llvm::unique_function<InFlightDiagnostic()> |
32 | getDefaultDiagnosticEmitFn(MLIRContext *ctx); |
33 | llvm::unique_function<InFlightDiagnostic()> |
34 | getDefaultDiagnosticEmitFn(const Location &loc); |
35 | |
36 | //===----------------------------------------------------------------------===// |
37 | // StorageUserTraitBase |
38 | //===----------------------------------------------------------------------===// |
39 | |
40 | /// Helper class for implementing traits for storage classes. Clients are not |
41 | /// expected to interact with this directly, so its members are all protected. |
42 | template <typename ConcreteType, template <typename> class TraitType> |
43 | class StorageUserTraitBase { |
44 | protected: |
45 | /// Return the derived instance. |
46 | ConcreteType getInstance() const { |
47 | // We have to cast up to the trait type, then to the concrete type because |
48 | // the concrete type will multiply derive from the (content free) TraitBase |
49 | // class, and we need to be able to disambiguate the path for the C++ |
50 | // compiler. |
51 | auto *trait = static_cast<const TraitType<ConcreteType> *>(this); |
52 | return *static_cast<const ConcreteType *>(trait); |
53 | } |
54 | }; |
55 | |
56 | namespace StorageUserTrait { |
57 | /// This trait is used to determine if a storage user, like Type, is mutable |
58 | /// or not. A storage user is mutable if ImplType of the derived class defines |
59 | /// a `mutate` function with a proper signature. Note that this trait is not |
60 | /// supposed to be used publicly. Users should use alias names like |
61 | /// `TypeTrait::IsMutable` instead. |
62 | template <typename ConcreteType> |
63 | struct IsMutable : public StorageUserTraitBase<ConcreteType, IsMutable> {}; |
64 | } // namespace StorageUserTrait |
65 | |
66 | //===----------------------------------------------------------------------===// |
67 | // StorageUserBase |
68 | //===----------------------------------------------------------------------===// |
69 | |
70 | namespace storage_user_base_impl { |
71 | /// Returns true if this given Trait ID matches the IDs of any of the provided |
72 | /// trait types `Traits`. |
73 | template <template <typename T> class... Traits> |
74 | bool hasTrait(TypeID traitID) { |
75 | TypeID traitIDs[] = {TypeID::get<Traits>()...}; |
76 | for (unsigned i = 0, e = sizeof...(Traits); i != e; ++i) |
77 | if (traitIDs[i] == traitID) |
78 | return true; |
79 | return false; |
80 | } |
81 | |
82 | // We specialize for the empty case to not define an empty array. |
83 | template <> |
84 | inline bool hasTrait(TypeID traitID) { |
85 | return false; |
86 | } |
87 | } // namespace storage_user_base_impl |
88 | |
89 | /// Utility class for implementing users of storage classes uniqued by a |
90 | /// StorageUniquer. Clients are not expected to interact with this class |
91 | /// directly. |
92 | template <typename ConcreteT, typename BaseT, typename StorageT, |
93 | typename UniquerT, template <typename T> class... Traits> |
94 | class StorageUserBase : public BaseT, public Traits<ConcreteT>... { |
95 | public: |
96 | using BaseT::BaseT; |
97 | |
98 | /// Utility declarations for the concrete attribute class. |
99 | using Base = StorageUserBase<ConcreteT, BaseT, StorageT, UniquerT, Traits...>; |
100 | using ImplType = StorageT; |
101 | using HasTraitFn = bool (*)(TypeID); |
102 | |
103 | /// Return a unique identifier for the concrete type. |
104 | static TypeID getTypeID() { return TypeID::get<ConcreteT>(); } |
105 | |
106 | /// Provide an implementation of 'classof' that compares the type id of the |
107 | /// provided value with that of the concrete type. |
108 | template <typename T> |
109 | static bool classof(T val) { |
110 | static_assert(std::is_convertible<ConcreteT, T>::value, |
111 | "casting from a non-convertible type"); |
112 | return val.getTypeID() == getTypeID(); |
113 | } |
114 | |
115 | /// Returns an interface map for the interfaces registered to this storage |
116 | /// user. This should not be used directly. |
117 | static detail::InterfaceMap getInterfaceMap() { |
118 | return detail::InterfaceMap::template get<Traits<ConcreteT>...>(); |
119 | } |
120 | |
121 | /// Returns the function that returns true if the given Trait ID matches the |
122 | /// IDs of any of the traits defined by the storage user. |
123 | static HasTraitFn getHasTraitFn() { |
124 | return [](TypeID id) { |
125 | return storage_user_base_impl::hasTrait<Traits...>(id); |
126 | }; |
127 | } |
128 | |
129 | /// Attach the given models as implementations of the corresponding interfaces |
130 | /// for the concrete storage user class. The type must be registered with the |
131 | /// context, i.e. the dialect to which the type belongs must be loaded. The |
132 | /// call will abort otherwise. |
133 | template <typename... IfaceModels> |
134 | static void attachInterface(MLIRContext &context) { |
135 | typename ConcreteT::AbstractTy *abstract = |
136 | ConcreteT::AbstractTy::lookupMutable(TypeID::get<ConcreteT>(), |
137 | &context); |
138 | if (!abstract) |
139 | llvm::report_fatal_error("Registering an interface for an attribute/type " |
140 | "that is not itself registered."); |
141 | |
142 | (checkInterfaceTarget<IfaceModels>(), ...); |
143 | abstract->interfaceMap.template insert<IfaceModels...>(); |
144 | } |
145 | |
146 | /// Get or create a new ConcreteT instance within the ctx. This |
147 | /// function is guaranteed to return a non null object and will assert if |
148 | /// the arguments provided are invalid. |
149 | template <typename... Args> |
150 | static ConcreteT get(MLIRContext *ctx, Args... args) { |
151 | // Ensure that the invariants are correct for construction. |
152 | assert((static_cast <bool> (succeeded(ConcreteT::verify(getDefaultDiagnosticEmitFn (ctx), args...))) ? void (0) : __assert_fail ("succeeded(ConcreteT::verify(getDefaultDiagnosticEmitFn(ctx), args...))" , "mlir/include/mlir/IR/StorageUniquerSupport.h", 153, __extension__ __PRETTY_FUNCTION__)) |
153 | succeeded(ConcreteT::verify(getDefaultDiagnosticEmitFn(ctx), args...)))(static_cast <bool> (succeeded(ConcreteT::verify(getDefaultDiagnosticEmitFn (ctx), args...))) ? void (0) : __assert_fail ("succeeded(ConcreteT::verify(getDefaultDiagnosticEmitFn(ctx), args...))" , "mlir/include/mlir/IR/StorageUniquerSupport.h", 153, __extension__ __PRETTY_FUNCTION__)); |
154 | return UniquerT::template get<ConcreteT>(ctx, args...); |
155 | } |
156 | |
157 | /// Get or create a new ConcreteT instance within the ctx, defined at |
158 | /// the given, potentially unknown, location. If the arguments provided are |
159 | /// invalid, errors are emitted using the provided location and a null object |
160 | /// is returned. |
161 | template <typename... Args> |
162 | static ConcreteT getChecked(const Location &loc, Args... args) { |
163 | return ConcreteT::getChecked(getDefaultDiagnosticEmitFn(loc), args...); |
164 | } |
165 | |
166 | /// Get or create a new ConcreteT instance within the ctx. If the arguments |
167 | /// provided are invalid, errors are emitted using the provided `emitError` |
168 | /// and a null object is returned. |
169 | template <typename... Args> |
170 | static ConcreteT getChecked(function_ref<InFlightDiagnostic()> emitErrorFn, |
171 | MLIRContext *ctx, Args... args) { |
172 | // If the construction invariants fail then we return a null attribute. |
173 | if (failed(ConcreteT::verify(emitErrorFn, args...))) |
174 | return ConcreteT(); |
175 | return UniquerT::template get<ConcreteT>(ctx, args...); |
176 | } |
177 | |
178 | /// Get an instance of the concrete type from a void pointer. |
179 | static ConcreteT getFromOpaquePointer(const void *ptr) { |
180 | return ConcreteT((const typename BaseT::ImplType *)ptr); |
181 | } |
182 | |
183 | protected: |
184 | /// Mutate the current storage instance. This will not change the unique key. |
185 | /// The arguments are forwarded to 'ConcreteT::mutate'. |
186 | template <typename... Args> |
187 | LogicalResult mutate(Args &&...args) { |
188 | static_assert(std::is_base_of<StorageUserTrait::IsMutable<ConcreteT>, |
189 | ConcreteT>::value, |
190 | "The `mutate` function expects mutable trait " |
191 | "(e.g. TypeTrait::IsMutable) to be attached on parent."); |
192 | return UniquerT::template mutate<ConcreteT>(this->getContext(), getImpl(), |
193 | std::forward<Args>(args)...); |
194 | } |
195 | |
196 | /// Default implementation that just returns success. |
197 | template <typename... Args> |
198 | static LogicalResult verify(Args... args) { |
199 | return success(); |
200 | } |
201 | |
202 | /// Utility for easy access to the storage instance. |
203 | ImplType *getImpl() const { return static_cast<ImplType *>(this->impl); } |
204 | |
205 | private: |
206 | /// Trait to check if T provides a 'ConcreteEntity' type alias. |
207 | template <typename T> |
208 | using has_concrete_entity_t = typename T::ConcreteEntity; |
209 | |
210 | /// A struct-wrapped type alias to T::ConcreteEntity if provided and to |
211 | /// ConcreteT otherwise. This is akin to std::conditional but doesn't fail on |
212 | /// the missing typedef. Useful for checking if the interface is targeting the |
213 | /// right class. |
214 | template <typename T, |
215 | bool = llvm::is_detected<has_concrete_entity_t, T>::value> |
216 | struct IfaceTargetOrConcreteT { |
217 | using type = typename T::ConcreteEntity; |
218 | }; |
219 | template <typename T> |
220 | struct IfaceTargetOrConcreteT<T, false> { |
221 | using type = ConcreteT; |
222 | }; |
223 | |
224 | /// A hook for static assertion that the external interface model T is |
225 | /// targeting a base class of the concrete attribute/type. The model can also |
226 | /// be a fallback model that works for every attribute/type. |
227 | template <typename T> |
228 | static void checkInterfaceTarget() { |
229 | static_assert(std::is_base_of<typename IfaceTargetOrConcreteT<T>::type, |
230 | ConcreteT>::value, |
231 | "attaching an interface to the wrong attribute/type kind"); |
232 | } |
233 | }; |
234 | } // namespace detail |
235 | } // namespace mlir |
236 | |
237 | #endif |