Bug Summary

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

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name DataLayoutInterfaces.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/build/llvm-toolchain-snapshot-16~++20220904122748+c444af1c20b3/build-llvm/tools/clang/stage2-bins -resource-dir /usr/lib/llvm-16/lib/clang/16.0.0 -D MLIR_CUDA_CONVERSIONS_ENABLED=1 -D MLIR_ROCM_CONVERSIONS_ENABLED=1 -D _DEBUG -D _GNU_SOURCE -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -I tools/mlir/lib/Interfaces -I /build/llvm-toolchain-snapshot-16~++20220904122748+c444af1c20b3/mlir/lib/Interfaces -I include -I /build/llvm-toolchain-snapshot-16~++20220904122748+c444af1c20b3/llvm/include -I /build/llvm-toolchain-snapshot-16~++20220904122748+c444af1c20b3/mlir/include -I tools/mlir/include -D _FORTIFY_SOURCE=2 -D NDEBUG -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/backward -internal-isystem /usr/lib/llvm-16/lib/clang/16.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/build/llvm-toolchain-snapshot-16~++20220904122748+c444af1c20b3/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fmacro-prefix-map=/build/llvm-toolchain-snapshot-16~++20220904122748+c444af1c20b3/= -fcoverage-prefix-map=/build/llvm-toolchain-snapshot-16~++20220904122748+c444af1c20b3/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fcoverage-prefix-map=/build/llvm-toolchain-snapshot-16~++20220904122748+c444af1c20b3/= -O2 -Wno-unused-command-line-argument -Wno-unused-parameter -Wwrite-strings -Wno-missing-field-initializers -Wno-long-long -Wno-maybe-uninitialized -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wno-comment -Wno-misleading-indentation -std=c++17 -fdeprecated-macro -fdebug-compilation-dir=/build/llvm-toolchain-snapshot-16~++20220904122748+c444af1c20b3/build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/llvm-toolchain-snapshot-16~++20220904122748+c444af1c20b3/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/llvm-toolchain-snapshot-16~++20220904122748+c444af1c20b3/= -ferror-limit 19 -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-09-04-125545-48738-1 -x c++ /build/llvm-toolchain-snapshot-16~++20220904122748+c444af1c20b3/mlir/lib/Interfaces/DataLayoutInterfaces.cpp

/build/llvm-toolchain-snapshot-16~++20220904122748+c444af1c20b3/mlir/lib/Interfaces/DataLayoutInterfaces.cpp

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
18using 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.
37static 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
44unsigned
45mlir::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
51unsigned 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
89static DataLayoutEntryInterface
90findEntryForIntegerType(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
105static unsigned extractABIAlignment(DataLayoutEntryInterface entry) {
106 auto values =
107 entry.getValue().cast<DenseIntElementsAttr>().getValues<int32_t>();
108 return *values.begin() / 8u;
109}
110
111static unsigned
112getIntegerTypeABIAlignment(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
123static unsigned
124getFloatTypeABIAlignment(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
133unsigned 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
160static 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
166static unsigned
167getIntegerTypePreferredAlignment(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
176static unsigned
177getFloatTypePreferredAlignment(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
186unsigned 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>())
5
Taking false branch
191 return dataLayout.getTypeABIAlignment(type);
192
193 if (auto fltType = type.dyn_cast<FloatType>())
6
Assuming pointer value is null
7
Taking false branch
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>())
8
Calling 'Type::dyn_cast'
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
216DataLayoutEntryList
217mlir::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
226DataLayoutEntryInterface
227mlir::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
237static 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`.
249static void
250collectParentLayouts(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.
281static 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
311LogicalResult 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
337template <typename OpTy>
338void 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
346mlir::DataLayout::DataLayout() : DataLayout(ModuleOp()) {}
347
348mlir::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
356mlir::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
364mlir::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
377void 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.
401static 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
411unsigned 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
423unsigned 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
435unsigned 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
447unsigned mlir::DataLayout::getTypePreferredAlignment(Type t) const {
448 checkValid();
449 return cachedLookup(t, preferredAlignments, [&](Type ty) {
450 DataLayoutEntryList list;
451 if (originalLayout)
1
Assuming the condition is false
2
Taking false branch
452 list = originalLayout.getSpecForType(ty.getTypeID());
453 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
3
Taking false branch
454 return iface.getTypePreferredAlignment(ty, *this, list);
455 return detail::getDefaultPreferredAlignment(ty, *this, list);
4
Calling 'getDefaultPreferredAlignment'
456 });
457}
458
459//===----------------------------------------------------------------------===//
460// DataLayoutSpecInterface
461//===----------------------------------------------------------------------===//
462
463void 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
474LogicalResult 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"

/build/llvm-toolchain-snapshot-16~++20220904122748+c444af1c20b3/mlir/include/mlir/IR/Types.h

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
17namespace 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.
72class Type {
73public:
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(); }
15
Called C++ object pointer is null
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
189protected:
190 ImplType *impl{nullptr};
191};
192
193inline raw_ostream &operator<<(raw_ostream &os, Type type) {
194 type.print(os);
195 return os;
196}
197
198//===----------------------------------------------------------------------===//
199// TypeTraitBase
200//===----------------------------------------------------------------------===//
201
202namespace TypeTrait {
203/// This class represents the base of a type trait.
204template <typename ConcreteType, template <typename> class TraitType>
205using 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.
214template <typename ConcreteType, typename Traits>
215class TypeInterface : public detail::Interface<ConcreteType, Type, Traits, Type,
216 TypeTrait::TraitBase> {
217public:
218 using Base = TypeInterface<ConcreteType, Traits>;
219 using InterfaceBase =
220 detail::Interface<ConcreteType, Type, Traits, Type, TypeTrait::TraitBase>;
221 using InterfaceBase::InterfaceBase;
222
223private:
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.
240namespace TypeTrait {
241template <typename ConcreteType>
242using IsMutable = detail::StorageUserTrait::IsMutable<ConcreteType>;
243} // namespace TypeTrait
244
245//===----------------------------------------------------------------------===//
246// Type Utils
247//===----------------------------------------------------------------------===//
248
249// Make Type hashable.
250inline ::llvm::hash_code hash_value(Type arg) {
251 return DenseMapInfo<const Type::ImplType *>::getHashValue(arg.impl);
252}
253
254template <typename... Tys>
255bool Type::isa() const {
256 return llvm::isa<Tys...>(*this);
257}
258
259template <typename... Tys>
260bool Type::isa_and_nonnull() const {
261 return llvm::isa_and_present<Tys...>(*this);
262}
263
264template <typename U>
265U Type::dyn_cast() const {
266 return llvm::dyn_cast<U>(*this);
9
Calling 'dyn_cast<mlir::IntegerType, mlir::Type>'
267}
268
269template <typename U>
270U Type::dyn_cast_or_null() const {
271 return llvm::dyn_cast_or_null<U>(*this);
272}
273
274template <typename U>
275U Type::cast() const {
276 return llvm::cast<U>(*this);
277}
278
279} // namespace mlir
280
281namespace llvm {
282
283// Type hash just like pointers.
284template <>
285struct 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};
297template <typename T>
298struct 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.
312template <>
313struct PointerLikeTypeTraits<mlir::Type> {
314public:
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
327template <typename To, typename From>
328struct 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>>
11.1
'is_same_v' is false
11.1
'is_same_v' is false
11.1
'is_same_v' is false
11.1
'is_same_v' is false
||
345 std::is_base_of_v<To, From>
11.2
'is_base_of_v' is false
11.2
'is_base_of_v' is false
11.2
'is_base_of_v' is false
11.2
'is_base_of_v' is false
|| To::classof(ty);
12
Value assigned to 'val.impl'
13
Calling 'StorageUserBase::classof'
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

/build/llvm-toolchain-snapshot-16~++20220904122748+c444af1c20b3/llvm/include/llvm/Support/Casting.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
24namespace 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.
34template <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
41template <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.
63template <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.
68template <typename To, typename From>
69struct 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
73template <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
79template <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
85template <typename To, typename From>
86struct 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
93template <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
100template <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
107template <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
114template <typename To, typename From>
115struct 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
122template <typename To, typename From, typename SimpleFrom>
123struct 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
133template <typename To, typename FromTy>
134struct 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
145template <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.
149template <class To, class From> struct cast_retty_impl {
150 using ret_type = To &; // Normal case, return Ty&
151};
152template <class To, class From> struct cast_retty_impl<To, const From> {
153 using ret_type = const To &; // Normal case, return Ty&
154};
155
156template <class To, class From> struct cast_retty_impl<To, From *> {
157 using ret_type = To *; // Pointer arg case, return Ty*
158};
159
160template <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
164template <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
168template <class To, class From>
169struct cast_retty_impl<To, std::unique_ptr<From>> {
170private:
171 using PointerType = typename cast_retty_impl<To, From *>::ret_type;
172 using ResultType = std::remove_pointer_t<PointerType>;
173
174public:
175 using ret_type = std::unique_ptr<ResultType>;
176};
177
178template <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
185template <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
190template <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//
202template <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
211template <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
219template <class To, class FromTy>
220struct 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
232template <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/// };
252template <typename To, typename From, typename Enable = void>
253struct 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.
265template <typename To, typename From>
266struct 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.
277template <typename To, typename From>
278struct 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`.
302template <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.
309template <typename To, typename From, typename Derived>
310struct DefaultDoCastIfPossible {
311 static To doCastIfPossible(From f) {
312 if (!Derived::isPossible(f))
11
Calling 'CastInfo::isPossible'
313 return Derived::castFailed();
314 return Derived::doCast(f);
315 }
316};
317
318namespace 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.
321template <typename OptionalDerived, typename Default>
322using 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.
329template <typename To, typename From, typename Derived = void>
330struct 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.
343template <typename To, typename From, typename Derived = void>
344struct 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>.
365template <typename To, typename From, typename Derived = void>
366struct 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///
388template <typename To, typename From, typename ForwardTo>
389struct 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///
423template <typename To, typename From, typename ForwardTo>
424struct 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
476template <typename To, typename From, typename Enable = void>
477struct 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.
503template <typename To, typename From>
504struct 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.
533template <typename To, typename From>
534struct 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.
539template <typename To, typename From>
540struct 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)) { ... }
547template <typename To, typename From>
548[[nodiscard]] inline bool isa(const From &Val) {
549 return CastInfo<To, const From>::isPossible(Val);
550}
551
552template <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
564template <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
570template <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
576template <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
582template <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
596template <typename To, typename From>
597[[nodiscard]] inline decltype(auto) dyn_cast(const From &Val) {
598 return CastInfo<To, const From>::doCastIfPossible(Val);
10
Calling 'DefaultDoCastIfPossible::doCastIfPossible'
599}
600
601template <typename To, typename From>
602[[nodiscard]] inline decltype(auto) dyn_cast(From &Val) {
603 return CastInfo<To, From>::doCastIfPossible(Val);
604}
605
606template <typename To, typename From>
607[[nodiscard]] inline decltype(auto) dyn_cast(From *Val) {
608 return CastInfo<To, From *>::doCastIfPossible(Val);
609}
610
611template <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
620template <typename T>
621constexpr 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.
631template <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.
638template <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.
646template <typename T>
647struct 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
653namespace 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.
656template <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.
662template <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.
669template <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
676template <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.
683template <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
691template <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
698template <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
705template <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.
715template <class X, class Y> auto cast_or_null(const Y &Val) {
716 return cast_if_present<X>(Val);
717}
718
719template <class X, class Y> auto cast_or_null(Y &Val) {
720 return cast_if_present<X>(Val);
721}
722
723template <class X, class Y> auto cast_or_null(Y *Val) {
724 return cast_if_present<X>(Val);
725}
726
727template <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.
733template <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
739template <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
745template <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.
754template <class X, class Y> auto dyn_cast_or_null(const Y &Val) {
755 return dyn_cast_if_present<X>(Val);
756}
757
758template <class X, class Y> auto dyn_cast_or_null(Y &Val) {
759 return dyn_cast_if_present<X>(Val);
760}
761
762template <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.
771template <class X, class Y>
772[[nodiscard]] inline typename CastInfo<X, std::unique_ptr<Y>>::CastResultType
773unique_dyn_cast(std::unique_ptr<Y> &Val) {
774 if (!isa<X>(Val))
775 return nullptr;
776 return cast<X>(std::move(Val));
777}
778
779template <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.
786template <class X, class Y>
787[[nodiscard]] inline typename CastInfo<X, std::unique_ptr<Y>>::CastResultType
788unique_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
794template <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

/build/llvm-toolchain-snapshot-16~++20220904122748+c444af1c20b3/mlir/include/mlir/IR/StorageUniquerSupport.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
22namespace mlir {
23class InFlightDiagnostic;
24class Location;
25class MLIRContext;
26
27namespace 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.
31llvm::unique_function<InFlightDiagnostic()>
32getDefaultDiagnosticEmitFn(MLIRContext *ctx);
33llvm::unique_function<InFlightDiagnostic()>
34getDefaultDiagnosticEmitFn(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.
42template <typename ConcreteType, template <typename> class TraitType>
43class StorageUserTraitBase {
44protected:
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
56namespace 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.
62template <typename ConcreteType>
63struct IsMutable : public StorageUserTraitBase<ConcreteType, IsMutable> {};
64} // namespace StorageUserTrait
65
66//===----------------------------------------------------------------------===//
67// StorageUserBase
68//===----------------------------------------------------------------------===//
69
70namespace storage_user_base_impl {
71/// Returns true if this given Trait ID matches the IDs of any of the provided
72/// trait types `Traits`.
73template <template <typename T> class... Traits>
74bool 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.
83template <>
84inline 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.
92template <typename ConcreteT, typename BaseT, typename StorageT,
93 typename UniquerT, template <typename T> class... Traits>
94class StorageUserBase : public BaseT, public Traits<ConcreteT>... {
95public:
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();
14
Calling 'Type::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
183protected:
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
205private:
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