Bug Summary

File:build/llvm-toolchain-snapshot-16~++20220904122748+c444af1c20b3/mlir/lib/Dialect/SPIRV/IR/SPIRVDialect.cpp
Warning:line 348, column 10
2nd function call argument is an uninitialized value

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 SPIRVDialect.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/Dialect/SPIRV/IR -I /build/llvm-toolchain-snapshot-16~++20220904122748+c444af1c20b3/mlir/lib/Dialect/SPIRV/IR -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/Dialect/SPIRV/IR/SPIRVDialect.cpp

/build/llvm-toolchain-snapshot-16~++20220904122748+c444af1c20b3/mlir/lib/Dialect/SPIRV/IR/SPIRVDialect.cpp

1//===- LLVMDialect.cpp - MLIR SPIR-V dialect ------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM
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 SPIR-V dialect in MLIR.
10//
11//===----------------------------------------------------------------------===//
12
13#include "mlir/Dialect/SPIRV/IR/SPIRVDialect.h"
14#include "mlir/Dialect/SPIRV/IR/ParserUtils.h"
15#include "mlir/Dialect/SPIRV/IR/SPIRVOps.h"
16#include "mlir/Dialect/SPIRV/IR/SPIRVTypes.h"
17#include "mlir/Dialect/SPIRV/IR/TargetAndABI.h"
18#include "mlir/IR/Builders.h"
19#include "mlir/IR/BuiltinTypes.h"
20#include "mlir/IR/DialectImplementation.h"
21#include "mlir/IR/MLIRContext.h"
22#include "mlir/Parser/Parser.h"
23#include "mlir/Transforms/InliningUtils.h"
24#include "llvm/ADT/DenseMap.h"
25#include "llvm/ADT/Sequence.h"
26#include "llvm/ADT/SetVector.h"
27#include "llvm/ADT/StringExtras.h"
28#include "llvm/ADT/StringMap.h"
29#include "llvm/ADT/StringSwitch.h"
30#include "llvm/ADT/TypeSwitch.h"
31#include "llvm/Support/raw_ostream.h"
32
33using namespace mlir;
34using namespace mlir::spirv;
35
36#include "mlir/Dialect/SPIRV/IR/SPIRVOpsDialect.cpp.inc"
37
38//===----------------------------------------------------------------------===//
39// InlinerInterface
40//===----------------------------------------------------------------------===//
41
42/// Returns true if the given region contains spv.Return or spv.ReturnValue ops.
43static inline bool containsReturn(Region &region) {
44 return llvm::any_of(region, [](Block &block) {
45 Operation *terminator = block.getTerminator();
46 return isa<spirv::ReturnOp, spirv::ReturnValueOp>(terminator);
47 });
48}
49
50namespace {
51/// This class defines the interface for inlining within the SPIR-V dialect.
52struct SPIRVInlinerInterface : public DialectInlinerInterface {
53 using DialectInlinerInterface::DialectInlinerInterface;
54
55 /// All call operations within SPIRV can be inlined.
56 bool isLegalToInline(Operation *call, Operation *callable,
57 bool wouldBeCloned) const final {
58 return true;
59 }
60
61 /// Returns true if the given region 'src' can be inlined into the region
62 /// 'dest' that is attached to an operation registered to the current dialect.
63 bool isLegalToInline(Region *dest, Region *src, bool wouldBeCloned,
64 BlockAndValueMapping &) const final {
65 // Return true here when inlining into spv.func, spv.mlir.selection, and
66 // spv.mlir.loop operations.
67 auto *op = dest->getParentOp();
68 return isa<spirv::FuncOp, spirv::SelectionOp, spirv::LoopOp>(op);
69 }
70
71 /// Returns true if the given operation 'op', that is registered to this
72 /// dialect, can be inlined into the region 'dest' that is attached to an
73 /// operation registered to the current dialect.
74 bool isLegalToInline(Operation *op, Region *dest, bool wouldBeCloned,
75 BlockAndValueMapping &) const final {
76 // TODO: Enable inlining structured control flows with return.
77 if ((isa<spirv::SelectionOp, spirv::LoopOp>(op)) &&
78 containsReturn(op->getRegion(0)))
79 return false;
80 // TODO: we need to filter OpKill here to avoid inlining it to
81 // a loop continue construct:
82 // https://github.com/KhronosGroup/SPIRV-Headers/issues/86
83 // However OpKill is fragment shader specific and we don't support it yet.
84 return true;
85 }
86
87 /// Handle the given inlined terminator by replacing it with a new operation
88 /// as necessary.
89 void handleTerminator(Operation *op, Block *newDest) const final {
90 if (auto returnOp = dyn_cast<spirv::ReturnOp>(op)) {
91 OpBuilder(op).create<spirv::BranchOp>(op->getLoc(), newDest);
92 op->erase();
93 } else if (auto retValOp = dyn_cast<spirv::ReturnValueOp>(op)) {
94 llvm_unreachable("unimplemented spv.ReturnValue in inliner")::llvm::llvm_unreachable_internal("unimplemented spv.ReturnValue in inliner"
, "mlir/lib/Dialect/SPIRV/IR/SPIRVDialect.cpp", 94)
;
95 }
96 }
97
98 /// Handle the given inlined terminator by replacing it with a new operation
99 /// as necessary.
100 void handleTerminator(Operation *op,
101 ArrayRef<Value> valuesToRepl) const final {
102 // Only spv.ReturnValue needs to be handled here.
103 auto retValOp = dyn_cast<spirv::ReturnValueOp>(op);
104 if (!retValOp)
105 return;
106
107 // Replace the values directly with the return operands.
108 assert(valuesToRepl.size() == 1 &&(static_cast <bool> (valuesToRepl.size() == 1 &&
"spv.ReturnValue expected to only handle one result") ? void
(0) : __assert_fail ("valuesToRepl.size() == 1 && \"spv.ReturnValue expected to only handle one result\""
, "mlir/lib/Dialect/SPIRV/IR/SPIRVDialect.cpp", 109, __extension__
__PRETTY_FUNCTION__))
109 "spv.ReturnValue expected to only handle one result")(static_cast <bool> (valuesToRepl.size() == 1 &&
"spv.ReturnValue expected to only handle one result") ? void
(0) : __assert_fail ("valuesToRepl.size() == 1 && \"spv.ReturnValue expected to only handle one result\""
, "mlir/lib/Dialect/SPIRV/IR/SPIRVDialect.cpp", 109, __extension__
__PRETTY_FUNCTION__))
;
110 valuesToRepl.front().replaceAllUsesWith(retValOp.value());
111 }
112};
113} // namespace
114
115//===----------------------------------------------------------------------===//
116// SPIR-V Dialect
117//===----------------------------------------------------------------------===//
118
119void SPIRVDialect::initialize() {
120 registerAttributes();
121 registerTypes();
122
123 // Add SPIR-V ops.
124 addOperations<
125#define GET_OP_LIST
126#include "mlir/Dialect/SPIRV/IR/SPIRVOps.cpp.inc"
127 >();
128
129 addInterfaces<SPIRVInlinerInterface>();
130
131 // Allow unknown operations because SPIR-V is extensible.
132 allowUnknownOperations();
133}
134
135std::string SPIRVDialect::getAttributeName(Decoration decoration) {
136 return llvm::convertToSnakeFromCamelCase(stringifyDecoration(decoration));
137}
138
139//===----------------------------------------------------------------------===//
140// Type Parsing
141//===----------------------------------------------------------------------===//
142
143// Forward declarations.
144template <typename ValTy>
145static Optional<ValTy> parseAndVerify(SPIRVDialect const &dialect,
146 DialectAsmParser &parser);
147template <>
148Optional<Type> parseAndVerify<Type>(SPIRVDialect const &dialect,
149 DialectAsmParser &parser);
150
151template <>
152Optional<unsigned> parseAndVerify<unsigned>(SPIRVDialect const &dialect,
153 DialectAsmParser &parser);
154
155static Type parseAndVerifyType(SPIRVDialect const &dialect,
156 DialectAsmParser &parser) {
157 Type type;
158 SMLoc typeLoc = parser.getCurrentLocation();
159 if (parser.parseType(type))
160 return Type();
161
162 // Allow SPIR-V dialect types
163 if (&type.getDialect() == &dialect)
164 return type;
165
166 // Check other allowed types
167 if (auto t = type.dyn_cast<FloatType>()) {
168 if (type.isBF16()) {
169 parser.emitError(typeLoc, "cannot use 'bf16' to compose SPIR-V types");
170 return Type();
171 }
172 } else if (auto t = type.dyn_cast<IntegerType>()) {
173 if (!ScalarType::isValid(t)) {
174 parser.emitError(typeLoc,
175 "only 1/8/16/32/64-bit integer type allowed but found ")
176 << type;
177 return Type();
178 }
179 } else if (auto t = type.dyn_cast<VectorType>()) {
180 if (t.getRank() != 1) {
181 parser.emitError(typeLoc, "only 1-D vector allowed but found ") << t;
182 return Type();
183 }
184 if (t.getNumElements() > 4) {
185 parser.emitError(
186 typeLoc, "vector length has to be less than or equal to 4 but found ")
187 << t.getNumElements();
188 return Type();
189 }
190 } else {
191 parser.emitError(typeLoc, "cannot use ")
192 << type << " to compose SPIR-V types";
193 return Type();
194 }
195
196 return type;
197}
198
199static Type parseAndVerifyMatrixType(SPIRVDialect const &dialect,
200 DialectAsmParser &parser) {
201 Type type;
202 SMLoc typeLoc = parser.getCurrentLocation();
203 if (parser.parseType(type))
204 return Type();
205
206 if (auto t = type.dyn_cast<VectorType>()) {
207 if (t.getRank() != 1) {
208 parser.emitError(typeLoc, "only 1-D vector allowed but found ") << t;
209 return Type();
210 }
211 if (t.getNumElements() > 4 || t.getNumElements() < 2) {
212 parser.emitError(typeLoc,
213 "matrix columns size has to be less than or equal "
214 "to 4 and greater than or equal 2, but found ")
215 << t.getNumElements();
216 return Type();
217 }
218
219 if (!t.getElementType().isa<FloatType>()) {
220 parser.emitError(typeLoc, "matrix columns' elements must be of "
221 "Float type, got ")
222 << t.getElementType();
223 return Type();
224 }
225 } else {
226 parser.emitError(typeLoc, "matrix must be composed using vector "
227 "type, got ")
228 << type;
229 return Type();
230 }
231
232 return type;
233}
234
235static Type parseAndVerifySampledImageType(SPIRVDialect const &dialect,
236 DialectAsmParser &parser) {
237 Type type;
238 SMLoc typeLoc = parser.getCurrentLocation();
239 if (parser.parseType(type))
240 return Type();
241
242 if (!type.isa<ImageType>()) {
243 parser.emitError(typeLoc,
244 "sampled image must be composed using image type, got ")
245 << type;
246 return Type();
247 }
248
249 return type;
250}
251
252/// Parses an optional `, stride = N` assembly segment. If no parsing failure
253/// occurs, writes `N` to `stride` if existing and writes 0 to `stride` if
254/// missing.
255static LogicalResult parseOptionalArrayStride(const SPIRVDialect &dialect,
256 DialectAsmParser &parser,
257 unsigned &stride) {
258 if (failed(parser.parseOptionalComma())) {
259 stride = 0;
260 return success();
261 }
262
263 if (parser.parseKeyword("stride") || parser.parseEqual())
264 return failure();
265
266 SMLoc strideLoc = parser.getCurrentLocation();
267 Optional<unsigned> optStride = parseAndVerify<unsigned>(dialect, parser);
268 if (!optStride)
269 return failure();
270
271 if (!(stride = *optStride)) {
272 parser.emitError(strideLoc, "ArrayStride must be greater than zero");
273 return failure();
274 }
275 return success();
276}
277
278// element-type ::= integer-type
279// | floating-point-type
280// | vector-type
281// | spirv-type
282//
283// array-type ::= `!spv.array` `<` integer-literal `x` element-type
284// (`,` `stride` `=` integer-literal)? `>`
285static Type parseArrayType(SPIRVDialect const &dialect,
286 DialectAsmParser &parser) {
287 if (parser.parseLess())
288 return Type();
289
290 SmallVector<int64_t, 1> countDims;
291 SMLoc countLoc = parser.getCurrentLocation();
292 if (parser.parseDimensionList(countDims, /*allowDynamic=*/false))
293 return Type();
294 if (countDims.size() != 1) {
295 parser.emitError(countLoc,
296 "expected single integer for array element count");
297 return Type();
298 }
299
300 // According to the SPIR-V spec:
301 // "Length is the number of elements in the array. It must be at least 1."
302 int64_t count = countDims[0];
303 if (count == 0) {
304 parser.emitError(countLoc, "expected array length greater than 0");
305 return Type();
306 }
307
308 Type elementType = parseAndVerifyType(dialect, parser);
309 if (!elementType)
310 return Type();
311
312 unsigned stride = 0;
313 if (failed(parseOptionalArrayStride(dialect, parser, stride)))
314 return Type();
315
316 if (parser.parseGreater())
317 return Type();
318 return ArrayType::get(elementType, count, stride);
319}
320
321// cooperative-matrix-type ::= `!spv.coopmatrix` `<` element-type ',' scope ','
322// rows ',' columns>`
323static Type parseCooperativeMatrixType(SPIRVDialect const &dialect,
324 DialectAsmParser &parser) {
325 if (parser.parseLess())
7
Taking false branch
326 return Type();
327
328 SmallVector<int64_t, 2> dims;
329 SMLoc countLoc = parser.getCurrentLocation();
330 if (parser.parseDimensionList(dims, /*allowDynamic=*/false))
8
Taking false branch
331 return Type();
332
333 if (dims.size() != 2) {
9
Assuming the condition is false
10
Taking false branch
334 parser.emitError(countLoc, "expected rows and columns size");
335 return Type();
336 }
337
338 auto elementTy = parseAndVerifyType(dialect, parser);
339 if (!elementTy)
11
Assuming the condition is false
12
Taking false branch
340 return Type();
341
342 Scope scope;
13
'scope' declared without an initial value
343 if (parser.parseComma() || parseEnumKeywordAttr(scope, parser, "scope <id>"))
14
Calling 'parseEnumKeywordAttr<mlir::spirv::Scope, mlir::DialectAsmParser>'
19
Returning from 'parseEnumKeywordAttr<mlir::spirv::Scope, mlir::DialectAsmParser>'
20
Taking false branch
344 return Type();
345
346 if (parser.parseGreater())
21
Taking false branch
347 return Type();
348 return CooperativeMatrixNVType::get(elementTy, scope, dims[0], dims[1]);
22
2nd function call argument is an uninitialized value
349}
350
351// joint-matrix-type ::= `!spv.jointmatrix` `<`rows `x` columns `x` element-type
352// `,` layout `,` scope`>`
353static Type parseJointMatrixType(SPIRVDialect const &dialect,
354 DialectAsmParser &parser) {
355 if (parser.parseLess())
356 return Type();
357
358 SmallVector<int64_t, 2> dims;
359 SMLoc countLoc = parser.getCurrentLocation();
360 if (parser.parseDimensionList(dims, /*allowDynamic=*/false))
361 return Type();
362
363 if (dims.size() != 2) {
364 parser.emitError(countLoc, "expected rows and columns size");
365 return Type();
366 }
367
368 auto elementTy = parseAndVerifyType(dialect, parser);
369 if (!elementTy)
370 return Type();
371 MatrixLayout matrixLayout;
372 if (parser.parseComma() ||
373 parseEnumKeywordAttr(matrixLayout, parser, "matrixLayout <id>"))
374 return Type();
375 Scope scope;
376 if (parser.parseComma() || parseEnumKeywordAttr(scope, parser, "scope <id>"))
377 return Type();
378 if (parser.parseGreater())
379 return Type();
380 return JointMatrixINTELType::get(elementTy, scope, dims[0], dims[1],
381 matrixLayout);
382}
383
384// TODO: Reorder methods to be utilities first and parse*Type
385// methods in alphabetical order
386//
387// storage-class ::= `UniformConstant`
388// | `Uniform`
389// | `Workgroup`
390// | <and other storage classes...>
391//
392// pointer-type ::= `!spv.ptr<` element-type `,` storage-class `>`
393static Type parsePointerType(SPIRVDialect const &dialect,
394 DialectAsmParser &parser) {
395 if (parser.parseLess())
396 return Type();
397
398 auto pointeeType = parseAndVerifyType(dialect, parser);
399 if (!pointeeType)
400 return Type();
401
402 StringRef storageClassSpec;
403 SMLoc storageClassLoc = parser.getCurrentLocation();
404 if (parser.parseComma() || parser.parseKeyword(&storageClassSpec))
405 return Type();
406
407 auto storageClass = symbolizeStorageClass(storageClassSpec);
408 if (!storageClass) {
409 parser.emitError(storageClassLoc, "unknown storage class: ")
410 << storageClassSpec;
411 return Type();
412 }
413 if (parser.parseGreater())
414 return Type();
415 return PointerType::get(pointeeType, *storageClass);
416}
417
418// runtime-array-type ::= `!spv.rtarray` `<` element-type
419// (`,` `stride` `=` integer-literal)? `>`
420static Type parseRuntimeArrayType(SPIRVDialect const &dialect,
421 DialectAsmParser &parser) {
422 if (parser.parseLess())
423 return Type();
424
425 Type elementType = parseAndVerifyType(dialect, parser);
426 if (!elementType)
427 return Type();
428
429 unsigned stride = 0;
430 if (failed(parseOptionalArrayStride(dialect, parser, stride)))
431 return Type();
432
433 if (parser.parseGreater())
434 return Type();
435 return RuntimeArrayType::get(elementType, stride);
436}
437
438// matrix-type ::= `!spv.matrix` `<` integer-literal `x` element-type `>`
439static Type parseMatrixType(SPIRVDialect const &dialect,
440 DialectAsmParser &parser) {
441 if (parser.parseLess())
442 return Type();
443
444 SmallVector<int64_t, 1> countDims;
445 SMLoc countLoc = parser.getCurrentLocation();
446 if (parser.parseDimensionList(countDims, /*allowDynamic=*/false))
447 return Type();
448 if (countDims.size() != 1) {
449 parser.emitError(countLoc, "expected single unsigned "
450 "integer for number of columns");
451 return Type();
452 }
453
454 int64_t columnCount = countDims[0];
455 // According to the specification, Matrices can have 2, 3, or 4 columns
456 if (columnCount < 2 || columnCount > 4) {
457 parser.emitError(countLoc, "matrix is expected to have 2, 3, or 4 "
458 "columns");
459 return Type();
460 }
461
462 Type columnType = parseAndVerifyMatrixType(dialect, parser);
463 if (!columnType)
464 return Type();
465
466 if (parser.parseGreater())
467 return Type();
468
469 return MatrixType::get(columnType, columnCount);
470}
471
472// Specialize this function to parse each of the parameters that define an
473// ImageType. By default it assumes this is an enum type.
474template <typename ValTy>
475static Optional<ValTy> parseAndVerify(SPIRVDialect const &dialect,
476 DialectAsmParser &parser) {
477 StringRef enumSpec;
478 SMLoc enumLoc = parser.getCurrentLocation();
479 if (parser.parseKeyword(&enumSpec)) {
480 return llvm::None;
481 }
482
483 auto val = spirv::symbolizeEnum<ValTy>(enumSpec);
484 if (!val)
485 parser.emitError(enumLoc, "unknown attribute: '") << enumSpec << "'";
486 return val;
487}
488
489template <>
490Optional<Type> parseAndVerify<Type>(SPIRVDialect const &dialect,
491 DialectAsmParser &parser) {
492 // TODO: Further verify that the element type can be sampled
493 auto ty = parseAndVerifyType(dialect, parser);
494 if (!ty)
495 return llvm::None;
496 return ty;
497}
498
499template <typename IntTy>
500static Optional<IntTy> parseAndVerifyInteger(SPIRVDialect const &dialect,
501 DialectAsmParser &parser) {
502 IntTy offsetVal = std::numeric_limits<IntTy>::max();
503 if (parser.parseInteger(offsetVal))
504 return llvm::None;
505 return offsetVal;
506}
507
508template <>
509Optional<unsigned> parseAndVerify<unsigned>(SPIRVDialect const &dialect,
510 DialectAsmParser &parser) {
511 return parseAndVerifyInteger<unsigned>(dialect, parser);
512}
513
514namespace {
515// Functor object to parse a comma separated list of specs. The function
516// parseAndVerify does the actual parsing and verification of individual
517// elements. This is a functor since parsing the last element of the list
518// (termination condition) needs partial specialization.
519template <typename ParseType, typename... Args>
520struct ParseCommaSeparatedList {
521 Optional<std::tuple<ParseType, Args...>>
522 operator()(SPIRVDialect const &dialect, DialectAsmParser &parser) const {
523 auto parseVal = parseAndVerify<ParseType>(dialect, parser);
524 if (!parseVal)
525 return llvm::None;
526
527 auto numArgs = std::tuple_size<std::tuple<Args...>>::value;
528 if (numArgs != 0 && failed(parser.parseComma()))
529 return llvm::None;
530 auto remainingValues = ParseCommaSeparatedList<Args...>{}(dialect, parser);
531 if (!remainingValues)
532 return llvm::None;
533 return std::tuple_cat(std::tuple<ParseType>(parseVal.value()),
534 remainingValues.value());
535 }
536};
537
538// Partial specialization of the function to parse a comma separated list of
539// specs to parse the last element of the list.
540template <typename ParseType>
541struct ParseCommaSeparatedList<ParseType> {
542 Optional<std::tuple<ParseType>> operator()(SPIRVDialect const &dialect,
543 DialectAsmParser &parser) const {
544 if (auto value = parseAndVerify<ParseType>(dialect, parser))
545 return std::tuple<ParseType>(*value);
546 return llvm::None;
547 }
548};
549} // namespace
550
551// dim ::= `1D` | `2D` | `3D` | `Cube` | <and other SPIR-V Dim specifiers...>
552//
553// depth-info ::= `NoDepth` | `IsDepth` | `DepthUnknown`
554//
555// arrayed-info ::= `NonArrayed` | `Arrayed`
556//
557// sampling-info ::= `SingleSampled` | `MultiSampled`
558//
559// sampler-use-info ::= `SamplerUnknown` | `NeedSampler` | `NoSampler`
560//
561// format ::= `Unknown` | `Rgba32f` | <and other SPIR-V Image formats...>
562//
563// image-type ::= `!spv.image<` element-type `,` dim `,` depth-info `,`
564// arrayed-info `,` sampling-info `,`
565// sampler-use-info `,` format `>`
566static Type parseImageType(SPIRVDialect const &dialect,
567 DialectAsmParser &parser) {
568 if (parser.parseLess())
569 return Type();
570
571 auto value =
572 ParseCommaSeparatedList<Type, Dim, ImageDepthInfo, ImageArrayedInfo,
573 ImageSamplingInfo, ImageSamplerUseInfo,
574 ImageFormat>{}(dialect, parser);
575 if (!value)
576 return Type();
577
578 if (parser.parseGreater())
579 return Type();
580 return ImageType::get(*value);
581}
582
583// sampledImage-type :: = `!spv.sampledImage<` image-type `>`
584static Type parseSampledImageType(SPIRVDialect const &dialect,
585 DialectAsmParser &parser) {
586 if (parser.parseLess())
587 return Type();
588
589 Type parsedType = parseAndVerifySampledImageType(dialect, parser);
590 if (!parsedType)
591 return Type();
592
593 if (parser.parseGreater())
594 return Type();
595 return SampledImageType::get(parsedType);
596}
597
598// Parse decorations associated with a member.
599static ParseResult parseStructMemberDecorations(
600 SPIRVDialect const &dialect, DialectAsmParser &parser,
601 ArrayRef<Type> memberTypes,
602 SmallVectorImpl<StructType::OffsetInfo> &offsetInfo,
603 SmallVectorImpl<StructType::MemberDecorationInfo> &memberDecorationInfo) {
604
605 // Check if the first element is offset.
606 SMLoc offsetLoc = parser.getCurrentLocation();
607 StructType::OffsetInfo offset = 0;
608 OptionalParseResult offsetParseResult = parser.parseOptionalInteger(offset);
609 if (offsetParseResult.has_value()) {
610 if (failed(*offsetParseResult))
611 return failure();
612
613 if (offsetInfo.size() != memberTypes.size() - 1) {
614 return parser.emitError(offsetLoc,
615 "offset specification must be given for "
616 "all members");
617 }
618 offsetInfo.push_back(offset);
619 }
620
621 // Check for no spirv::Decorations.
622 if (succeeded(parser.parseOptionalRSquare()))
623 return success();
624
625 // If there was an offset, make sure to parse the comma.
626 if (offsetParseResult.has_value() && parser.parseComma())
627 return failure();
628
629 // Check for spirv::Decorations.
630 auto parseDecorations = [&]() {
631 auto memberDecoration = parseAndVerify<spirv::Decoration>(dialect, parser);
632 if (!memberDecoration)
633 return failure();
634
635 // Parse member decoration value if it exists.
636 if (succeeded(parser.parseOptionalEqual())) {
637 auto memberDecorationValue =
638 parseAndVerifyInteger<uint32_t>(dialect, parser);
639
640 if (!memberDecorationValue)
641 return failure();
642
643 memberDecorationInfo.emplace_back(
644 static_cast<uint32_t>(memberTypes.size() - 1), 1,
645 memberDecoration.value(), memberDecorationValue.value());
646 } else {
647 memberDecorationInfo.emplace_back(
648 static_cast<uint32_t>(memberTypes.size() - 1), 0,
649 memberDecoration.value(), 0);
650 }
651 return success();
652 };
653 if (failed(parser.parseCommaSeparatedList(parseDecorations)) ||
654 failed(parser.parseRSquare()))
655 return failure();
656
657 return success();
658}
659
660// struct-member-decoration ::= integer-literal? spirv-decoration*
661// struct-type ::=
662// `!spv.struct<` (id `,`)?
663// `(`
664// (spirv-type (`[` struct-member-decoration `]`)?)*
665// `)>`
666static Type parseStructType(SPIRVDialect const &dialect,
667 DialectAsmParser &parser) {
668 // TODO: This function is quite lengthy. Break it down into smaller chunks.
669
670 // To properly resolve recursive references while parsing recursive struct
671 // types, we need to maintain a list of enclosing struct type names. This set
672 // maintains the names of struct types in which the type we are about to parse
673 // is nested.
674 //
675 // Note: This has to be thread_local to enable multiple threads to safely
676 // parse concurrently.
677 thread_local SetVector<StringRef> structContext;
678
679 static auto removeIdentifierAndFail = [](SetVector<StringRef> &structContext,
680 StringRef identifier) {
681 if (!identifier.empty())
682 structContext.remove(identifier);
683
684 return Type();
685 };
686
687 if (parser.parseLess())
688 return Type();
689
690 StringRef identifier;
691
692 // Check if this is an identified struct type.
693 if (succeeded(parser.parseOptionalKeyword(&identifier))) {
694 // Check if this is a possible recursive reference.
695 if (succeeded(parser.parseOptionalGreater())) {
696 if (structContext.count(identifier) == 0) {
697 parser.emitError(
698 parser.getNameLoc(),
699 "recursive struct reference not nested in struct definition");
700
701 return Type();
702 }
703
704 return StructType::getIdentified(dialect.getContext(), identifier);
705 }
706
707 if (failed(parser.parseComma()))
708 return Type();
709
710 if (structContext.count(identifier) != 0) {
711 parser.emitError(parser.getNameLoc(),
712 "identifier already used for an enclosing struct");
713
714 return removeIdentifierAndFail(structContext, identifier);
715 }
716
717 structContext.insert(identifier);
718 }
719
720 if (failed(parser.parseLParen()))
721 return removeIdentifierAndFail(structContext, identifier);
722
723 if (succeeded(parser.parseOptionalRParen()) &&
724 succeeded(parser.parseOptionalGreater())) {
725 if (!identifier.empty())
726 structContext.remove(identifier);
727
728 return StructType::getEmpty(dialect.getContext(), identifier);
729 }
730
731 StructType idStructTy;
732
733 if (!identifier.empty())
734 idStructTy = StructType::getIdentified(dialect.getContext(), identifier);
735
736 SmallVector<Type, 4> memberTypes;
737 SmallVector<StructType::OffsetInfo, 4> offsetInfo;
738 SmallVector<StructType::MemberDecorationInfo, 4> memberDecorationInfo;
739
740 do {
741 Type memberType;
742 if (parser.parseType(memberType))
743 return removeIdentifierAndFail(structContext, identifier);
744 memberTypes.push_back(memberType);
745
746 if (succeeded(parser.parseOptionalLSquare()))
747 if (parseStructMemberDecorations(dialect, parser, memberTypes, offsetInfo,
748 memberDecorationInfo))
749 return removeIdentifierAndFail(structContext, identifier);
750 } while (succeeded(parser.parseOptionalComma()));
751
752 if (!offsetInfo.empty() && memberTypes.size() != offsetInfo.size()) {
753 parser.emitError(parser.getNameLoc(),
754 "offset specification must be given for all members");
755 return removeIdentifierAndFail(structContext, identifier);
756 }
757
758 if (failed(parser.parseRParen()) || failed(parser.parseGreater()))
759 return removeIdentifierAndFail(structContext, identifier);
760
761 if (!identifier.empty()) {
762 if (failed(idStructTy.trySetBody(memberTypes, offsetInfo,
763 memberDecorationInfo)))
764 return Type();
765
766 structContext.remove(identifier);
767 return idStructTy;
768 }
769
770 return StructType::get(memberTypes, offsetInfo, memberDecorationInfo);
771}
772
773// spirv-type ::= array-type
774// | element-type
775// | image-type
776// | pointer-type
777// | runtime-array-type
778// | sampled-image-type
779// | struct-type
780Type SPIRVDialect::parseType(DialectAsmParser &parser) const {
781 StringRef keyword;
782 if (parser.parseKeyword(&keyword))
1
Taking false branch
783 return Type();
784
785 if (keyword == "array")
2
Assuming the condition is false
3
Taking false branch
786 return parseArrayType(*this, parser);
787 if (keyword == "coopmatrix")
4
Assuming the condition is true
5
Taking true branch
788 return parseCooperativeMatrixType(*this, parser);
6
Calling 'parseCooperativeMatrixType'
789 if (keyword == "jointmatrix")
790 return parseJointMatrixType(*this, parser);
791 if (keyword == "image")
792 return parseImageType(*this, parser);
793 if (keyword == "ptr")
794 return parsePointerType(*this, parser);
795 if (keyword == "rtarray")
796 return parseRuntimeArrayType(*this, parser);
797 if (keyword == "sampled_image")
798 return parseSampledImageType(*this, parser);
799 if (keyword == "struct")
800 return parseStructType(*this, parser);
801 if (keyword == "matrix")
802 return parseMatrixType(*this, parser);
803 parser.emitError(parser.getNameLoc(), "unknown SPIR-V type: ") << keyword;
804 return Type();
805}
806
807//===----------------------------------------------------------------------===//
808// Type Printing
809//===----------------------------------------------------------------------===//
810
811static void print(ArrayType type, DialectAsmPrinter &os) {
812 os << "array<" << type.getNumElements() << " x " << type.getElementType();
813 if (unsigned stride = type.getArrayStride())
814 os << ", stride=" << stride;
815 os << ">";
816}
817
818static void print(RuntimeArrayType type, DialectAsmPrinter &os) {
819 os << "rtarray<" << type.getElementType();
820 if (unsigned stride = type.getArrayStride())
821 os << ", stride=" << stride;
822 os << ">";
823}
824
825static void print(PointerType type, DialectAsmPrinter &os) {
826 os << "ptr<" << type.getPointeeType() << ", "
827 << stringifyStorageClass(type.getStorageClass()) << ">";
828}
829
830static void print(ImageType type, DialectAsmPrinter &os) {
831 os << "image<" << type.getElementType() << ", " << stringifyDim(type.getDim())
832 << ", " << stringifyImageDepthInfo(type.getDepthInfo()) << ", "
833 << stringifyImageArrayedInfo(type.getArrayedInfo()) << ", "
834 << stringifyImageSamplingInfo(type.getSamplingInfo()) << ", "
835 << stringifyImageSamplerUseInfo(type.getSamplerUseInfo()) << ", "
836 << stringifyImageFormat(type.getImageFormat()) << ">";
837}
838
839static void print(SampledImageType type, DialectAsmPrinter &os) {
840 os << "sampled_image<" << type.getImageType() << ">";
841}
842
843static void print(StructType type, DialectAsmPrinter &os) {
844 thread_local SetVector<StringRef> structContext;
845
846 os << "struct<";
847
848 if (type.isIdentified()) {
849 os << type.getIdentifier();
850
851 if (structContext.count(type.getIdentifier())) {
852 os << ">";
853 return;
854 }
855
856 os << ", ";
857 structContext.insert(type.getIdentifier());
858 }
859
860 os << "(";
861
862 auto printMember = [&](unsigned i) {
863 os << type.getElementType(i);
864 SmallVector<spirv::StructType::MemberDecorationInfo, 0> decorations;
865 type.getMemberDecorations(i, decorations);
866 if (type.hasOffset() || !decorations.empty()) {
867 os << " [";
868 if (type.hasOffset()) {
869 os << type.getMemberOffset(i);
870 if (!decorations.empty())
871 os << ", ";
872 }
873 auto eachFn = [&os](spirv::StructType::MemberDecorationInfo decoration) {
874 os << stringifyDecoration(decoration.decoration);
875 if (decoration.hasValue) {
876 os << "=" << decoration.decorationValue;
877 }
878 };
879 llvm::interleaveComma(decorations, os, eachFn);
880 os << "]";
881 }
882 };
883 llvm::interleaveComma(llvm::seq<unsigned>(0, type.getNumElements()), os,
884 printMember);
885 os << ")>";
886
887 if (type.isIdentified())
888 structContext.remove(type.getIdentifier());
889}
890
891static void print(CooperativeMatrixNVType type, DialectAsmPrinter &os) {
892 os << "coopmatrix<" << type.getRows() << "x" << type.getColumns() << "x";
893 os << type.getElementType() << ", " << stringifyScope(type.getScope());
894 os << ">";
895}
896
897static void print(JointMatrixINTELType type, DialectAsmPrinter &os) {
898 os << "jointmatrix<" << type.getRows() << "x" << type.getColumns() << "x";
899 os << type.getElementType() << ", "
900 << stringifyMatrixLayout(type.getMatrixLayout());
901 os << ", " << stringifyScope(type.getScope()) << ">";
902}
903
904static void print(MatrixType type, DialectAsmPrinter &os) {
905 os << "matrix<" << type.getNumColumns() << " x " << type.getColumnType();
906 os << ">";
907}
908
909void SPIRVDialect::printType(Type type, DialectAsmPrinter &os) const {
910 TypeSwitch<Type>(type)
911 .Case<ArrayType, CooperativeMatrixNVType, JointMatrixINTELType,
912 PointerType, RuntimeArrayType, ImageType, SampledImageType,
913 StructType, MatrixType>([&](auto type) { print(type, os); })
914 .Default([](Type) { llvm_unreachable("unhandled SPIR-V type")::llvm::llvm_unreachable_internal("unhandled SPIR-V type", "mlir/lib/Dialect/SPIRV/IR/SPIRVDialect.cpp"
, 914)
; });
915}
916
917//===----------------------------------------------------------------------===//
918// Constant
919//===----------------------------------------------------------------------===//
920
921Operation *SPIRVDialect::materializeConstant(OpBuilder &builder,
922 Attribute value, Type type,
923 Location loc) {
924 if (!spirv::ConstantOp::isBuildableWith(type))
925 return nullptr;
926
927 return builder.create<spirv::ConstantOp>(loc, type, value);
928}
929
930//===----------------------------------------------------------------------===//
931// Shader Interface ABI
932//===----------------------------------------------------------------------===//
933
934LogicalResult SPIRVDialect::verifyOperationAttribute(Operation *op,
935 NamedAttribute attribute) {
936 StringRef symbol = attribute.getName().strref();
937 Attribute attr = attribute.getValue();
938
939 if (symbol == spirv::getEntryPointABIAttrName()) {
940 if (!attr.isa<spirv::EntryPointABIAttr>()) {
941 return op->emitError("'")
942 << symbol << "' attribute must be an entry point ABI attribute";
943 }
944 } else if (symbol == spirv::getTargetEnvAttrName()) {
945 if (!attr.isa<spirv::TargetEnvAttr>())
946 return op->emitError("'") << symbol << "' must be a spirv::TargetEnvAttr";
947 } else {
948 return op->emitError("found unsupported '")
949 << symbol << "' attribute on operation";
950 }
951
952 return success();
953}
954
955/// Verifies the given SPIR-V `attribute` attached to a value of the given
956/// `valueType` is valid.
957static LogicalResult verifyRegionAttribute(Location loc, Type valueType,
958 NamedAttribute attribute) {
959 StringRef symbol = attribute.getName().strref();
960 Attribute attr = attribute.getValue();
961
962 if (symbol != spirv::getInterfaceVarABIAttrName())
963 return emitError(loc, "found unsupported '")
964 << symbol << "' attribute on region argument";
965
966 auto varABIAttr = attr.dyn_cast<spirv::InterfaceVarABIAttr>();
967 if (!varABIAttr)
968 return emitError(loc, "'")
969 << symbol << "' must be a spirv::InterfaceVarABIAttr";
970
971 if (varABIAttr.getStorageClass() && !valueType.isIntOrIndexOrFloat())
972 return emitError(loc, "'") << symbol
973 << "' attribute cannot specify storage class "
974 "when attaching to a non-scalar value";
975
976 return success();
977}
978
979LogicalResult SPIRVDialect::verifyRegionArgAttribute(Operation *op,
980 unsigned regionIndex,
981 unsigned argIndex,
982 NamedAttribute attribute) {
983 return verifyRegionAttribute(
984 op->getLoc(), op->getRegion(regionIndex).getArgument(argIndex).getType(),
985 attribute);
986}
987
988LogicalResult SPIRVDialect::verifyRegionResultAttribute(
989 Operation *op, unsigned /*regionIndex*/, unsigned /*resultIndex*/,
990 NamedAttribute attribute) {
991 return op->emitError("cannot attach SPIR-V attributes to region result");
992}

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

1//===------------ ParserUtils.h - Parse text to SPIR-V ops ----------------===//
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 utilities used for parsing types and ops for SPIR-V
10// dialect.
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef MLIR_DIALECT_SPIRV_IR_PARSERUTILS_H_
15#define MLIR_DIALECT_SPIRV_IR_PARSERUTILS_H_
16
17#include "mlir/Dialect/SPIRV/IR/SPIRVAttributes.h"
18#include "mlir/IR/OpDefinition.h"
19#include "mlir/IR/OpImplementation.h"
20
21namespace mlir {
22
23/// Parses the next keyword in `parser` as an enumerant of the given
24/// `EnumClass`.
25template <typename EnumClass, typename ParserType>
26static ParseResult
27parseEnumKeywordAttr(EnumClass &value, ParserType &parser,
28 StringRef attrName = spirv::attributeName<EnumClass>()) {
29 StringRef keyword;
30 SmallVector<NamedAttribute, 1> attr;
31 auto loc = parser.getCurrentLocation();
32 if (parser.parseKeyword(&keyword))
15
Taking false branch
33 return failure();
34 if (Optional<EnumClass> attr = spirv::symbolizeEnum<EnumClass>(keyword)) {
16
Assuming the condition is false
17
Taking false branch
35 value = *attr;
36 return success();
37 }
38 return parser.emitError(loc, "invalid ")
18
Returning without writing to 'value'
39 << attrName << " attribute specification: " << keyword;
40}
41
42} // namespace mlir
43
44#endif // MLIR_DIALECT_SPIRV_IR_PARSERUTILS_H_