MLIR 24.0.0git
MemoryOps.cpp
Go to the documentation of this file.
1//===- MemoryOps.cpp - MLIR SPIR-V Memory 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// Defines the memory operations in the SPIR-V dialect.
10//
11//===----------------------------------------------------------------------===//
12
15
16#include "SPIRVOpUtils.h"
17#include "SPIRVParsingUtils.h"
19#include "mlir/IR/Diagnostics.h"
20
21#include "llvm/ADT/StringExtras.h"
22#include "llvm/Support/Casting.h"
23
24using namespace mlir::spirv::AttrNames;
25
26namespace mlir::spirv {
27
28/// Parses optional memory access (a.k.a. memory operand) attributes attached to
29/// a memory access operand/pointer. Specifically, parses the following syntax:
30/// (`[` memory-access `]`)?
31/// where:
32/// memory-access ::= `"None"` | `"Volatile"` | `"Aligned", `
33/// integer-literal | `"NonTemporal"`
34template <typename MemoryOpTy>
36 OperationState &state) {
37 // Parse an optional list of attributes staring with '['
38 if (parser.parseOptionalLSquare()) {
39 // Nothing to do
40 return success();
41 }
42
43 spirv::MemoryAccess memoryAccessAttr;
44 StringAttr memoryAccessAttrName =
45 MemoryOpTy::getMemoryAccessAttrName(state.name);
47 memoryAccessAttr, parser, state, memoryAccessAttrName))
48 return failure();
49
50 if (spirv::bitEnumContainsAll(memoryAccessAttr,
51 spirv::MemoryAccess::Aligned)) {
52 // Parse integer attribute for alignment.
53 Attribute alignmentAttr;
54 StringAttr alignmentAttrName = MemoryOpTy::getAlignmentAttrName(state.name);
55 Type i32Type = parser.getBuilder().getIntegerType(32);
56 if (parser.parseComma() ||
57 parser.parseAttribute(alignmentAttr, i32Type, alignmentAttrName,
58 state.attributes)) {
59 return failure();
60 }
61 }
62 return parser.parseRSquare();
63}
64
65// TODO Make sure to merge this and the previous function into one template
66// parameterized by memory access attribute name and alignment. Doing so now
67// results in VS2017 in producing an internal error (at the call site) that's
68// not detailed enough to understand what is happening.
69template <typename MemoryOpTy>
71 OperationState &state) {
72 // Parse an optional list of attributes staring with '['
73 if (parser.parseOptionalLSquare()) {
74 // Nothing to do
75 return success();
76 }
77
78 spirv::MemoryAccess memoryAccessAttr;
79 StringRef memoryAccessAttrName =
80 MemoryOpTy::getSourceMemoryAccessAttrName(state.name);
82 memoryAccessAttr, parser, state, memoryAccessAttrName))
83 return failure();
84
85 if (spirv::bitEnumContainsAll(memoryAccessAttr,
86 spirv::MemoryAccess::Aligned)) {
87 // Parse integer attribute for alignment.
88 Attribute alignmentAttr;
89 StringAttr alignmentAttrName =
90 MemoryOpTy::getSourceAlignmentAttrName(state.name);
91 Type i32Type = parser.getBuilder().getIntegerType(32);
92 if (parser.parseComma() ||
93 parser.parseAttribute(alignmentAttr, i32Type, alignmentAttrName,
94 state.attributes)) {
95 return failure();
96 }
97 }
98 return parser.parseRSquare();
99}
100
101// TODO Make sure to merge this and the previous function into one template
102// parameterized by memory access attribute name and alignment. Doing so now
103// results in VS2017 in producing an internal error (at the call site) that's
104// not detailed enough to understand what is happening.
105template <typename MemoryOpTy>
107 MemoryOpTy memoryOp, OpAsmPrinter &printer,
108 SmallVectorImpl<StringRef> &elidedAttrs,
109 std::optional<spirv::MemoryAccess> memoryAccessAtrrValue = std::nullopt,
110 std::optional<uint32_t> alignmentAttrValue = std::nullopt) {
111
112 printer << ", ";
113
114 // Print optional memory access attribute.
115 if (auto memAccess = (memoryAccessAtrrValue ? memoryAccessAtrrValue
116 : memoryOp.getMemoryAccess())) {
117 elidedAttrs.push_back(memoryOp.getSourceMemoryAccessAttrName());
118
119 printer << " [\"" << stringifyMemoryAccess(*memAccess) << "\"";
120
121 if (spirv::bitEnumContainsAll(*memAccess, spirv::MemoryAccess::Aligned)) {
122 // Print integer alignment attribute.
123 if (auto alignment = (alignmentAttrValue ? alignmentAttrValue
124 : memoryOp.getAlignment())) {
125 elidedAttrs.push_back(memoryOp.getSourceAlignmentAttrName());
126 printer << ", " << *alignment;
127 }
128 }
129 printer << "]";
130 }
131 elidedAttrs.push_back(spirv::attributeName<spirv::StorageClass>());
132}
133
134template <typename MemoryOpTy>
136 MemoryOpTy memoryOp, OpAsmPrinter &printer,
137 SmallVectorImpl<StringRef> &elidedAttrs,
138 std::optional<spirv::MemoryAccess> memoryAccessAtrrValue = std::nullopt,
139 std::optional<uint32_t> alignmentAttrValue = std::nullopt) {
140 // Print optional memory access attribute.
141 if (auto memAccess = (memoryAccessAtrrValue ? memoryAccessAtrrValue
142 : memoryOp.getMemoryAccess())) {
143 elidedAttrs.push_back(memoryOp.getMemoryAccessAttrName());
144
145 printer << " [\"" << stringifyMemoryAccess(*memAccess) << "\"";
146
147 if (spirv::bitEnumContainsAll(*memAccess, spirv::MemoryAccess::Aligned)) {
148 // Print integer alignment attribute.
149 if (auto alignment = (alignmentAttrValue ? alignmentAttrValue
150 : memoryOp.getAlignment())) {
151 elidedAttrs.push_back(memoryOp.getAlignmentAttrName());
152 printer << ", " << *alignment;
153 }
154 }
155 printer << "]";
156 }
157 elidedAttrs.push_back(spirv::attributeName<spirv::StorageClass>());
158}
159
160template <typename LoadStoreOpTy>
161static LogicalResult verifyLoadStorePtrAndValTypes(LoadStoreOpTy op, Value ptr,
162 Value val) {
163 // ODS already checks ptr is spirv::PointerType. Just check that the pointee
164 // type of the pointer and the type of the value are the same
165 //
166 // TODO: Check that the value type satisfies restrictions of
167 // SPIR-V OpLoad/OpStore operations
168 if (val.getType() !=
169 cast<spirv::PointerType>(ptr.getType()).getPointeeType()) {
170 return op.emitOpError("mismatch in result type and pointer type");
171 }
172 return success();
173}
174
175template <typename MemoryOpTy>
176static LogicalResult verifyMemoryAccessAttribute(MemoryOpTy memoryOp) {
177 // ODS checks for attributes values. Just need to verify that if the
178 // memory-access attribute is Aligned, then the alignment attribute must be
179 // present.
180 spirv::MemoryAccessAttr memAccessAttr = memoryOp.getMemoryAccessAttr();
181 if (!memAccessAttr) {
182 // Alignment attribute shouldn't be present if memory access attribute is
183 // not present.
184 if (memoryOp.getAlignmentAttr()) {
185 return memoryOp.emitOpError(
186 "invalid alignment specification without aligned memory access "
187 "specification");
188 }
189 return success();
190 }
191
192 spirv::MemoryAccessAttr memAccess = memAccessAttr;
193
194 if (!memAccess) {
195 return memoryOp.emitOpError("invalid memory access specifier: ")
196 << memAccessAttr;
197 }
198
199 if (spirv::bitEnumContainsAll(memAccess.getValue(),
200 spirv::MemoryAccess::Aligned)) {
201 if (!memoryOp.getAlignmentAttr()) {
202 return memoryOp.emitOpError("missing alignment value");
203 }
204 } else {
205 if (memoryOp.getAlignmentAttr()) {
206 return memoryOp.emitOpError(
207 "invalid alignment specification with non-aligned memory access "
208 "specification");
209 }
210 }
211 return success();
212}
213
214// TODO Make sure to merge this and the previous function into one template
215// parameterized by memory access attribute name and alignment. Doing so now
216// results in VS2017 in producing an internal error (at the call site) that's
217// not detailed enough to understand what is happening.
218template <typename MemoryOpTy>
219static LogicalResult verifySourceMemoryAccessAttribute(MemoryOpTy memoryOp) {
220 // ODS checks for attributes values. Just need to verify that if the
221 // memory-access attribute is Aligned, then the alignment attribute must be
222 // present.
223 spirv::MemoryAccessAttr memAccessAttr = memoryOp.getSourceMemoryAccessAttr();
224 if (!memAccessAttr) {
225 // Alignment attribute shouldn't be present if memory access attribute is
226 // not present.
227 if (memoryOp.getSourceAlignmentAttr()) {
228 return memoryOp.emitOpError(
229 "invalid alignment specification without aligned memory access "
230 "specification");
231 }
232 return success();
233 }
234
235 spirv::MemoryAccessAttr memAccess = memAccessAttr;
236
237 if (!memAccess) {
238 return memoryOp.emitOpError("invalid memory access specifier: ")
239 << memAccess;
240 }
241
242 if (spirv::bitEnumContainsAll(memAccess.getValue(),
243 spirv::MemoryAccess::Aligned)) {
244 if (!memoryOp.getSourceAlignmentAttr()) {
245 return memoryOp.emitOpError("missing alignment value");
246 }
247 } else {
248 if (memoryOp.getSourceAlignmentAttr()) {
249 return memoryOp.emitOpError(
250 "invalid alignment specification with non-aligned memory access "
251 "specification");
252 }
253 }
254 return success();
255}
256
257//===----------------------------------------------------------------------===//
258// spirv.AccessChainOp
259//===----------------------------------------------------------------------===//
260
262 auto ptrType = dyn_cast<spirv::PointerType>(type);
263 if (!ptrType) {
264 emitError(baseLoc, "'spirv.AccessChain' op expected a pointer "
265 "to composite type, but provided ")
266 << type;
267 return nullptr;
268 }
269
270 auto resultType = ptrType.getPointeeType();
271 auto resultStorageClass = ptrType.getStorageClass();
272 int32_t index = 0;
273
274 for (auto indexSSA : indices) {
275 auto cType = dyn_cast<spirv::CompositeType>(resultType);
276 if (!cType) {
277 emitError(
278 baseLoc,
279 "'spirv.AccessChain' op cannot extract from non-composite type ")
280 << resultType << " with index " << index;
281 return nullptr;
282 }
283 index = 0;
284 if (isa<spirv::StructType>(resultType)) {
285 Operation *op = indexSSA.getDefiningOp();
286 if (!op) {
287 emitError(baseLoc, "'spirv.AccessChain' op index must be an "
288 "integer spirv.Constant to access "
289 "element of spirv.struct");
290 return nullptr;
291 }
292
293 // TODO: this should be relaxed to allow
294 // integer literals of other bitwidths.
295 if (failed(spirv::extractValueFromConstOp(op, index))) {
296 emitError(
297 baseLoc,
298 "'spirv.AccessChain' index must be an integer spirv.Constant to "
299 "access element of spirv.struct, but provided ")
300 << op->getName();
301 return nullptr;
302 }
303 if (index < 0 || static_cast<uint64_t>(index) >= cType.getNumElements()) {
304 emitError(baseLoc, "'spirv.AccessChain' op index ")
305 << index << " out of bounds for " << resultType;
306 return nullptr;
307 }
308 }
309 resultType = cType.getElementType(index);
310 }
311 return spirv::PointerType::get(resultType, resultStorageClass);
312}
313
314void AccessChainOp::build(OpBuilder &builder, OperationState &state,
315 Value basePtr, ValueRange indices) {
316 auto type = getElementPtrType(basePtr.getType(), indices, state.location);
317 assert(type && "Unable to deduce return type based on basePtr and indices");
318 build(builder, state, type, basePtr, indices);
319}
320
321template <typename Op>
323 printer << ' ' << op.getBasePtr() << '[' << indices
324 << "] : " << op.getBasePtr().getType() << ", " << indices.getTypes();
325}
326
327template <typename Op>
328static LogicalResult verifyAccessChain(Op accessChainOp, ValueRange indices) {
329 auto resultType = getElementPtrType(accessChainOp.getBasePtr().getType(),
330 indices, accessChainOp.getLoc());
331 if (!resultType)
332 return failure();
333
334 auto providedResultType =
335 dyn_cast<spirv::PointerType>(accessChainOp.getType());
336 if (!providedResultType)
337 return accessChainOp.emitOpError(
338 "result type must be a pointer, but provided")
339 << providedResultType;
340
341 if (resultType != providedResultType)
342 return accessChainOp.emitOpError("invalid result type: expected ")
343 << resultType << ", but provided " << providedResultType;
344
345 return success();
346}
347
348LogicalResult AccessChainOp::verify() {
349 return verifyAccessChain(*this, getIndices());
350}
351
352//===----------------------------------------------------------------------===//
353// spirv.InBoundsAccessChainOp
354//===----------------------------------------------------------------------===//
355
356void InBoundsAccessChainOp::build(OpBuilder &builder, OperationState &state,
357 Value basePtr, ValueRange indices) {
358 Type type = getElementPtrType(basePtr.getType(), indices, state.location);
359 assert(type && "Unable to deduce return type based on basePtr and indices");
360 build(builder, state, type, basePtr, indices);
361}
362
363LogicalResult InBoundsAccessChainOp::verify() {
364 return verifyAccessChain(*this, getIndices());
365}
366
367//===----------------------------------------------------------------------===//
368// spirv.LoadOp
369//===----------------------------------------------------------------------===//
370
371void LoadOp::build(OpBuilder &builder, OperationState &state, Value basePtr,
372 MemoryAccessAttr memoryAccess, IntegerAttr alignment) {
373 auto ptrType = cast<spirv::PointerType>(basePtr.getType());
374 build(builder, state, ptrType.getPointeeType(), basePtr, memoryAccess,
375 alignment);
376}
377
378ParseResult LoadOp::parse(OpAsmParser &parser, OperationState &result) {
379 // Parse the storage class specification
380 spirv::StorageClass storageClass;
381 OpAsmParser::UnresolvedOperand ptrInfo;
382 Type elementType;
383 if (parseEnumStrAttr(storageClass, parser) || parser.parseOperand(ptrInfo) ||
385 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
386 parser.parseType(elementType)) {
387 return failure();
388 }
389
390 auto ptrType = spirv::PointerType::get(elementType, storageClass);
391 if (parser.resolveOperand(ptrInfo, ptrType, result.operands)) {
392 return failure();
393 }
394
395 result.addTypes(elementType);
396 return success();
397}
398
399void LoadOp::print(OpAsmPrinter &printer) {
400 SmallVector<StringRef, 4> elidedAttrs;
401 StringRef sc = stringifyStorageClass(
402 cast<spirv::PointerType>(getPtr().getType()).getStorageClass());
403 printer << " \"" << sc << "\" " << getPtr();
404
405 printMemoryAccessAttribute(*this, printer, elidedAttrs);
406
407 printer.printOptionalAttrDict(
408 (*this)->getDiscardableAttrDictionary().getValue(), elidedAttrs);
409 printer << " : " << getType();
410}
411
412LogicalResult LoadOp::verify() {
413 // SPIR-V spec : "Result Type is the type of the loaded object. It must be a
414 // type with fixed size; i.e., it cannot be, nor include, any
415 // OpTypeRuntimeArray types."
416 if (failed(verifyLoadStorePtrAndValTypes(*this, getPtr(), getValue()))) {
417 return failure();
418 }
419 return verifyMemoryAccessAttribute(*this);
420}
421
422//===----------------------------------------------------------------------===//
423// spirv.StoreOp
424//===----------------------------------------------------------------------===//
425
426ParseResult StoreOp::parse(OpAsmParser &parser, OperationState &result) {
427 // Parse the storage class specification
428 spirv::StorageClass storageClass;
429 SmallVector<OpAsmParser::UnresolvedOperand, 2> operandInfo;
430 auto loc = parser.getCurrentLocation();
431 Type elementType;
432 if (parseEnumStrAttr(storageClass, parser) ||
433 parser.parseOperandList(operandInfo, 2) ||
435 parser.parseColon() || parser.parseType(elementType)) {
436 return failure();
437 }
438
439 auto ptrType = spirv::PointerType::get(elementType, storageClass);
440 if (parser.resolveOperands(operandInfo, {ptrType, elementType}, loc,
441 result.operands)) {
442 return failure();
443 }
444 return success();
445}
446
447void StoreOp::print(OpAsmPrinter &printer) {
448 SmallVector<StringRef, 4> elidedAttrs;
449 StringRef sc = stringifyStorageClass(
450 cast<spirv::PointerType>(getPtr().getType()).getStorageClass());
451 printer << " \"" << sc << "\" " << getPtr() << ", " << getValue();
452
453 printMemoryAccessAttribute(*this, printer, elidedAttrs);
454
455 printer << " : " << getValue().getType();
456 printer.printOptionalAttrDict(
457 (*this)->getDiscardableAttrDictionary().getValue(), elidedAttrs);
458}
459
460LogicalResult StoreOp::verify() {
461 // SPIR-V spec : "Pointer is the pointer to store through. Its type must be an
462 // OpTypePointer whose Type operand is the same as the type of Object."
463 if (failed(verifyLoadStorePtrAndValTypes(*this, getPtr(), getValue())))
464 return failure();
465 return verifyMemoryAccessAttribute(*this);
466}
467
468//===----------------------------------------------------------------------===//
469// spirv.CopyMemory
470//===----------------------------------------------------------------------===//
471
472void CopyMemoryOp::print(OpAsmPrinter &printer) {
473 printer << ' ';
474
475 StringRef targetStorageClass = stringifyStorageClass(
476 cast<spirv::PointerType>(getTarget().getType()).getStorageClass());
477 printer << " \"" << targetStorageClass << "\" " << getTarget() << ", ";
478
479 StringRef sourceStorageClass = stringifyStorageClass(
480 cast<spirv::PointerType>(getSource().getType()).getStorageClass());
481 printer << " \"" << sourceStorageClass << "\" " << getSource();
482
483 SmallVector<StringRef, 4> elidedAttrs;
484 printMemoryAccessAttribute(*this, printer, elidedAttrs);
485 printSourceMemoryAccessAttribute(*this, printer, elidedAttrs,
486 getSourceMemoryAccess(),
487 getSourceAlignment());
488
489 printer.printOptionalAttrDict(
490 (*this)->getDiscardableAttrDictionary().getValue(), elidedAttrs);
491
492 Type pointeeType =
493 cast<spirv::PointerType>(getTarget().getType()).getPointeeType();
494 printer << " : " << pointeeType;
495}
496
497ParseResult CopyMemoryOp::parse(OpAsmParser &parser, OperationState &result) {
498 spirv::StorageClass targetStorageClass;
499 OpAsmParser::UnresolvedOperand targetPtrInfo;
500
501 spirv::StorageClass sourceStorageClass;
502 OpAsmParser::UnresolvedOperand sourcePtrInfo;
503
504 Type elementType;
505
506 if (parseEnumStrAttr(targetStorageClass, parser) ||
507 parser.parseOperand(targetPtrInfo) || parser.parseComma() ||
508 parseEnumStrAttr(sourceStorageClass, parser) ||
509 parser.parseOperand(sourcePtrInfo) ||
511 return failure();
512 }
513
514 if (!parser.parseOptionalComma()) {
515 // Parse 2nd memory access attributes.
517 return failure();
518 }
519 }
520
521 if (parser.parseColon() || parser.parseType(elementType))
522 return failure();
523
524 if (parser.parseOptionalAttrDict(result.attributes))
525 return failure();
526
527 auto targetPtrType = spirv::PointerType::get(elementType, targetStorageClass);
528 auto sourcePtrType = spirv::PointerType::get(elementType, sourceStorageClass);
529
530 if (parser.resolveOperand(targetPtrInfo, targetPtrType, result.operands) ||
531 parser.resolveOperand(sourcePtrInfo, sourcePtrType, result.operands)) {
532 return failure();
533 }
534
535 return success();
536}
537
538LogicalResult CopyMemoryOp::verify() {
539 Type targetType =
540 cast<spirv::PointerType>(getTarget().getType()).getPointeeType();
541
542 Type sourceType =
543 cast<spirv::PointerType>(getSource().getType()).getPointeeType();
544
545 if (targetType != sourceType)
546 return emitOpError("both operands must be pointers to the same type");
547
549 return failure();
550
551 // TODO - According to the spec:
552 //
553 // If two masks are present, the first applies to Target and cannot include
554 // MakePointerVisible, and the second applies to Source and cannot include
555 // MakePointerAvailable.
556 //
557 // Add such verification here.
558
560}
561
562//===----------------------------------------------------------------------===//
563// spirv.InBoundsPtrAccessChainOp
564//===----------------------------------------------------------------------===//
565
566void InBoundsPtrAccessChainOp::build(OpBuilder &builder, OperationState &state,
567 Value basePtr, Value element,
569 auto type = getElementPtrType(basePtr.getType(), indices, state.location);
570 assert(type && "Unable to deduce return type based on basePtr and indices");
571 build(builder, state, type, basePtr, element, indices);
572}
573
574LogicalResult InBoundsPtrAccessChainOp::verify() {
575 return verifyAccessChain(*this, getIndices());
576}
577
578//===----------------------------------------------------------------------===//
579// spirv.PtrAccessChainOp
580//===----------------------------------------------------------------------===//
581
582void PtrAccessChainOp::build(OpBuilder &builder, OperationState &state,
583 Value basePtr, Value element, ValueRange indices) {
584 auto type = getElementPtrType(basePtr.getType(), indices, state.location);
585 assert(type && "Unable to deduce return type based on basePtr and indices");
586 build(builder, state, type, basePtr, element, indices);
587}
588
589LogicalResult PtrAccessChainOp::verify() {
590 return verifyAccessChain(*this, getIndices());
591}
592
593//===----------------------------------------------------------------------===//
594// spirv.Variable
595//===----------------------------------------------------------------------===//
596
597ParseResult VariableOp::parse(OpAsmParser &parser, OperationState &result) {
598 // Parse optional initializer
599 std::optional<OpAsmParser::UnresolvedOperand> initInfo;
600 if (succeeded(parser.parseOptionalKeyword("init"))) {
601 initInfo = OpAsmParser::UnresolvedOperand();
602 if (parser.parseLParen() || parser.parseOperand(*initInfo) ||
603 parser.parseRParen())
604 return failure();
605 }
606
607 if (parseVariableDecorations(parser, result)) {
608 return failure();
609 }
610
611 // Parse result pointer type
612 Type type;
613 if (parser.parseColon())
614 return failure();
615 auto loc = parser.getCurrentLocation();
616 if (parser.parseType(type))
617 return failure();
618
619 auto ptrType = dyn_cast<spirv::PointerType>(type);
620 if (!ptrType)
621 return parser.emitError(loc, "expected spirv.ptr type");
622 result.addTypes(ptrType);
623
624 // Resolve the initializer operand
625 if (initInfo) {
626 if (parser.resolveOperand(*initInfo, ptrType.getPointeeType(),
627 result.operands))
628 return failure();
629 }
630
631 auto attr = parser.getBuilder().getAttr<spirv::StorageClassAttr>(
632 ptrType.getStorageClass());
633 result.addAttribute(spirv::attributeName<spirv::StorageClass>(), attr);
634
635 return success();
636}
637
638void VariableOp::print(OpAsmPrinter &printer) {
639 SmallVector<StringRef, 4> elidedAttrs{
640 spirv::attributeName<spirv::StorageClass>()};
641 // Print optional initializer
642 if (getNumOperands() != 0)
643 printer << " init(" << getInitializer() << ")";
644
645 printVariableDecorations(*this, printer, elidedAttrs);
646 printer << " : " << getType();
647}
648
649LogicalResult VariableOp::verify() {
650 // SPIR-V spec: "Storage Class is the Storage Class of the memory holding the
651 // object. It cannot be Generic. It must be the same as the Storage Class
652 // operand of the Result Type."
653 if (getStorageClass() != spirv::StorageClass::Function) {
654 return emitOpError(
655 "can only be used to model function-level variables. Use "
656 "spirv.GlobalVariable for module-level variables.");
657 }
658
659 auto pointerType = cast<spirv::PointerType>(getPointer().getType());
660 if (getStorageClass() != pointerType.getStorageClass())
661 return emitOpError(
662 "storage class must match result pointer's storage class");
663
664 if (getNumOperands() != 0) {
665 // SPIR-V spec: "Initializer must be an <id> from a constant instruction or
666 // a global (module scope) OpVariable instruction".
667 auto *initOp = getOperand(0).getDefiningOp();
668 if (!initOp || !isa<spirv::ConstantOp, // for normal constant
669 spirv::ReferenceOfOp, // for spec constant
670 spirv::AddressOfOp>(initOp))
671 return emitOpError("initializer must be the result of a "
672 "constant or spirv.GlobalVariable op");
673 }
674
675 auto getDecorationAttr = [op = getOperation()](spirv::Decoration decoration) {
676 return op->getDiscardableAttr(spirv::getDecorationString(decoration));
677 };
678
679 // TODO: generate these strings using ODS.
680 for (auto decoration :
681 {spirv::Decoration::DescriptorSet, spirv::Decoration::Binding,
682 spirv::Decoration::BuiltIn}) {
683 if (auto attr = getDecorationAttr(decoration))
684 return emitOpError("cannot have '")
685 << spirv::getDecorationString(decoration)
686 << "' attribute (only allowed in spirv.GlobalVariable)";
687 }
688
690 getPointeeType())))
691 return failure();
692
693 return success();
694}
695
696} // namespace mlir::spirv
return success()
getNumOperands() - 1))) return failure()
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
virtual ParseResult parseOptionalKeyword(StringRef keyword)=0
Parse the given keyword if present.
virtual ParseResult parseRParen()=0
Parse a ) token.
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
virtual ParseResult parseRSquare()=0
Parse a ] token.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual ParseResult parseOptionalComma()=0
Parse a , token if present.
virtual ParseResult parseColon()=0
Parse a : token.
virtual ParseResult parseLParen()=0
Parse a ( token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseComma()=0
Parse a , token.
virtual ParseResult parseOptionalLSquare()=0
Parse a [ token if present.
virtual ParseResult parseAttribute(Attribute &result, Type type={})=0
Parse an arbitrary attribute of a given type and return it in result.
Attributes are known-constant values of operations.
Definition Attributes.h:25
IntegerType getIntegerType(unsigned width)
Definition Builders.cpp:75
Attr getAttr(Args &&...args)
Get or construct an instance of the attribute Attr with provided arguments.
Definition Builders.h:101
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual ParseResult resolveOperand(const UnresolvedOperand &operand, Type type, SmallVectorImpl< Value > &result)=0
Resolve an operand to an SSA value, emitting an error on failure.
ParseResult resolveOperands(Operands &&operands, Type type, SmallVectorImpl< Value > &result)
Resolve a list of operands to SSA values, emitting an error on failure, or appending the results to t...
virtual ParseResult parseOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single SSA value operand name along with a result number if allowResultNumber is true.
virtual ParseResult parseOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None, bool allowResultNumber=true, int requiredOperandCount=-1)=0
Parse zero or more SSA comma-separated operand references with a specified surrounding delimiter,...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
virtual void printOptionalAttrDict(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary with their values.
This class helps build Operations.
Definition Builders.h:210
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
Location getLoc()
The source location the operation was defined or derived from.
This provides public APIs that all operations should have.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
OperationName getName()
The name of an operation is the key identifier for it.
Definition Operation.h:115
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
static PointerType get(Type pointeeType, StorageClass storageClass)
Operation::operand_range getIndices(Operation *op)
Get the indices that the given load/store operation is operating on.
Definition Utils.cpp:18
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:732
static ParseResult parseSourceMemoryAccessAttributes(OpAsmParser &parser, OperationState &state)
Definition MemoryOps.cpp:70
ParseResult parseEnumStrAttr(EnumClass &value, OpAsmParser &parser, StringRef attrName=spirv::attributeName< EnumClass >())
Parses the next string attribute in parser as an enumerant of the given EnumClass.
static LogicalResult verifySourceMemoryAccessAttribute(MemoryOpTy memoryOp)
static void printSourceMemoryAccessAttribute(MemoryOpTy memoryOp, OpAsmPrinter &printer, SmallVectorImpl< StringRef > &elidedAttrs, std::optional< spirv::MemoryAccess > memoryAccessAtrrValue=std::nullopt, std::optional< uint32_t > alignmentAttrValue=std::nullopt)
ParseResult parseMemoryAccessAttributes(OpAsmParser &parser, OperationState &state)
Parses optional memory access (a.k.a.
Definition MemoryOps.cpp:35
static Type getElementPtrType(Type type, ValueRange indices, Location baseLoc)
void printVariableDecorations(Operation *op, OpAsmPrinter &printer, SmallVectorImpl< StringRef > &elidedAttrs)
Definition SPIRVOps.cpp:133
LogicalResult verifyPhysicalStorageBufferDecorations(Operation *op, Type pointeeType)
Verifies the SPV_KHR_physical_storage_buffer rule that a variable whose pointee is a pointer (or arra...
Definition SPIRVOps.cpp:93
static LogicalResult verifyLoadStorePtrAndValTypes(LoadStoreOpTy op, Value ptr, Value val)
static LogicalResult verifyMemoryAccessAttribute(MemoryOpTy memoryOp)
static void printAccessChain(Op op, ValueRange indices, OpAsmPrinter &printer)
static void printMemoryAccessAttribute(MemoryOpTy memoryOp, OpAsmPrinter &printer, SmallVectorImpl< StringRef > &elidedAttrs, std::optional< spirv::MemoryAccess > memoryAccessAtrrValue=std::nullopt, std::optional< uint32_t > alignmentAttrValue=std::nullopt)
LogicalResult extractValueFromConstOp(Operation *op, int32_t &value)
Definition SPIRVOps.cpp:49
std::string getDecorationString(Decoration decoration)
Converts a SPIR-V Decoration enum value to its snake_case string representation for use in MLIR attri...
ParseResult parseVariableDecorations(OpAsmParser &parser, OperationState &state)
static LogicalResult verifyAccessChain(Op accessChainOp, ValueRange indices)
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:307
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
This represents an operation in an abstracted form, suitable for use with the builder APIs.