MLIR 24.0.0git
XeGPUOps.cpp
Go to the documentation of this file.
1//===- XeGPUOps.cpp - MLIR XeGPU ops implementation -------------*- 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
14#include "mlir/IR/Builders.h"
17
18#include "llvm/Support/Debug.h"
19
20#define DEBUG_TYPE "xegpu"
21
22using namespace mlir;
23using namespace mlir::xegpu;
24
25template <typename T>
26static std::string makeString(T array, bool breakline = false) {
27 std::string buf;
28 buf.clear();
29 llvm::raw_string_ostream os(buf);
30 os << "[";
31 for (size_t i = 1; i < array.size(); i++) {
32 os << array[i - 1] << ", ";
33 if (breakline)
34 os << "\n\t\t";
35 }
36 os << array.back() << "]";
37 return buf;
38}
39
42 if (auto ty = llvm::dyn_cast<ShapedType>(type))
43 shape = SmallVector<int64_t>(ty.getShape());
44 else
45 shape.push_back(1);
46 return shape;
47}
48
49static bool isReadHintOrNone(const CachePolicyAttr &attr) {
50 if (!attr)
51 return true;
52 auto kind = attr.getValue();
53 return kind == CachePolicy::CACHED || kind == CachePolicy::UNCACHED ||
54 kind == CachePolicy::STREAMING || kind == CachePolicy::READ_INVALIDATE;
55}
56
57static bool isWriteHintOrNone(const CachePolicyAttr &attr) {
58 if (!attr)
59 return true;
60 auto kind = attr.getValue();
61 return kind == CachePolicy::CACHED || kind == CachePolicy::UNCACHED ||
62 kind == CachePolicy::WRITE_BACK || kind == CachePolicy::WRITE_THROUGH;
63}
64
65static LogicalResult
67 VectorType valueTy, int64_t chunkSize,
69
70 auto maskVecTy = dyn_cast<VectorType>(maskTy);
71 auto offsetsVecTy = dyn_cast<VectorType>(offsetsTy);
72 if (!valueTy) {
73 if (chunkSize > 1)
74 return emitError() << "Expecting chunk size == 1 for scalar result";
75 if (maskVecTy || offsetsVecTy)
76 return emitError() << "Expecting scalar mask and offsets.";
77 else if (maskVecTy && offsetsVecTy)
78 return emitError() << "Expecting a vector type result.";
79 return success();
80 }
81
82 auto valueSize = valueTy.getNumElements();
83 // SIMT mode with scalar mask and offsets.
84 if (!maskVecTy && !offsetsVecTy) {
85 if (valueSize != chunkSize)
86 return emitError() << "value elements must match chunk size "
87 << chunkSize;
88 return success();
89 }
90 auto maskShape = getShapeOf(maskTy);
91 auto valueShape = getShapeOf(valueTy);
92
93 if (!maskVecTy)
94 return emitError() << "Expecting a vector type mask.";
95 int64_t maskSize = maskVecTy.getNumElements();
96
97 if (chunkSize > 1) {
98 if ((valueTy.getRank() == 1) && (valueSize != chunkSize))
99 return emitError() << "value elements must match chunk size "
100 << chunkSize;
101 } else {
102 if (valueSize != maskSize)
103 return emitError()
104 << "Mask should match value except the chunk size dim.";
105 }
106 llvm::SmallVector<int64_t> expectedMaskShape(valueShape);
107 if (maskSize == 1)
108 return success();
109 if (chunkSize > 1)
110 expectedMaskShape.pop_back();
111 if (expectedMaskShape != maskShape)
112 return emitError() << "Mask should match value except the chunk size dim.";
113
114 return success();
115}
116
117// Validates the `contiguity` attribute against the op's offsets type: the
118// innermost offsets dimension is contiguous in runs of `size`, so `size` must
119// be >= 2 and must divide that dimension.
120static LogicalResult
121isValidContiguity(std::optional<uint64_t> contiguity, Type offsetsTy,
123 if (!contiguity)
124 return success();
125 auto offsetsVecTy = dyn_cast<VectorType>(offsetsTy);
126 if (!offsetsVecTy)
127 return emitError() << "contiguity requires vector offsets (one per lane).";
128 int64_t size = static_cast<int64_t>(*contiguity);
129 int64_t inner = offsetsVecTy.getShape().back();
130 if (size < 2)
131 return emitError() << "contiguity = " << size << " (must be >= 2)";
132 if (inner % size != 0)
133 return emitError() << "contiguity = " << size
134 << " (must divide the innermost offsets dim " << inner
135 << ")";
136 return success();
137}
138
139LogicalResult
140IsValidMatrixOpParams(VectorType dataTy, MemDescType mdescTy,
141 UnitAttr subgroup_block_io, DistributeLayoutAttr layout,
143
144 if (!dataTy) {
145 if (subgroup_block_io)
146 return emitError() << "subgroup_block_io "
147 "are only allowed when result is a VectorType.";
148 else
149 return success();
150 }
151
152 ArrayRef<int64_t> dataShape = dataTy.getShape();
153 ArrayRef<int64_t> mdescShape = mdescTy.getShape();
154
155 SmallVector<int64_t> blockShape = mdescTy.getBlockShape();
156 ArrayAttr strideAttr = mdescTy.getStrideAttr();
157 SmallVector<int64_t> strides;
158 for (Attribute attr : strideAttr.getValue()) {
159 strides.push_back(cast<IntegerAttr>(attr).getInt());
160 }
161 if (subgroup_block_io && layout) {
162 auto laneData = layout.getEffectiveLaneDataAsInt();
163 auto laneLayout = layout.getEffectiveLaneLayoutAsInt();
164 if (!laneData.empty()) {
165 bool isLaneDataContiguous =
166 std::all_of(laneData.begin(), std::prev(laneData.end()),
167 [](int x) { return x == 1; });
168 if (!isLaneDataContiguous)
169 return emitError() << "With subgroup_block_io, accessed data must be "
170 "contiguous and coalesced.";
171 for (size_t i = 0; i < laneData.size(); ++i) {
172 if (laneLayout[i] != blockShape[i])
173 return emitError() << "With subgroup_block_io, the block shape must "
174 "match the lane layout.";
175 if (laneLayout[i] != 1 && strides[i] != 1)
176 return emitError() << "With subgroup_block_io, the distributed "
177 "dimensions must be contiguous.";
178 }
179 }
180 }
181
182 if (layout && !layout.isDistributable(
183 SmallVector<int64_t>(dataShape.begin(), dataShape.end())))
184 return emitError() << "Value shape is not distributable with the layout";
185
186 if (dataShape.size() == mdescShape.size()) {
187 if (llvm::any_of(llvm::zip_equal(dataShape, mdescShape),
188 [](auto p) { return std::get<0>(p) > std::get<1>(p); }))
189 return emitError() << "data shape must not exceed mem_desc shape.";
190 }
191 // if the subgroup_block_io attribute is set, mdescTy must have block
192 // attribute
193 if (subgroup_block_io && !blockShape.size())
194 return emitError() << "mem_desc must have block attribute when "
195 "subgroup_block_io is set.";
196 return success();
197}
198
199//===----------------------------------------------------------------------===//
200// XeGPU_CreateMemDescOp
201//===----------------------------------------------------------------------===//
202LogicalResult CreateMemDescOp::verify() {
203 auto srcTy = getSource().getType();
205 return emitOpError("source memref must be contiguous.");
206 return success();
207}
208
209//===----------------------------------------------------------------------===//
210// XeGPU_CreateNdDescOp
211//===----------------------------------------------------------------------===//
212
213void CreateNdDescOp::build(OpBuilder &builder, OperationState &state,
214 Type tdesc, TypedValue<MemRefType> source) {
215 build(builder, state, tdesc, source, ValueRange({}) /* empty dynamic shape */,
216 ValueRange({}) /* empty dynamic strides */,
217 DenseI64ArrayAttr({}) /* empty const shape*/,
218 DenseI64ArrayAttr({}) /* empty const strides*/);
219}
220
221void CreateNdDescOp::build(OpBuilder &builder, OperationState &state,
222 Type tdesc, Value source,
225 Type srcTy = source.getType();
226 assert((isa<IntegerType, MemRefType>(srcTy)) &&
227 "Source has to be either int or memref.");
228
229 llvm::SmallVector<Value> dynamicShape;
230 llvm::SmallVector<Value> dynamicStrides;
231
232 llvm::SmallVector<int64_t> staticShape;
233 llvm::SmallVector<int64_t> staticStrides;
234
235 dispatchIndexOpFoldResults(shape, dynamicShape, staticShape);
236 dispatchIndexOpFoldResults(strides, dynamicStrides, staticStrides);
237
238 auto staticShapeAttr = builder.getDenseI64ArrayAttr(staticShape);
239 auto staticStridesAttr = builder.getDenseI64ArrayAttr(staticStrides);
240
241 if (auto memrefTy = dyn_cast<MemRefType>(srcTy)) {
242 auto memrefShape = memrefTy.getShape();
243 auto [memrefStrides, _] = memrefTy.getStridesAndOffset();
244
245 // if shape and strides are from Memref, we don't need attributes for them
246 // to keep the IR print clean (only do so for full-static case, otherwise
247 // printer would fail trying to print empty array-attr).
248 if (staticShape == memrefShape && staticStrides == memrefStrides &&
249 dynamicShape.empty() && dynamicStrides.empty()) {
250 staticShapeAttr = DenseI64ArrayAttr();
251 staticStridesAttr = DenseI64ArrayAttr();
252 }
253 }
254
255 build(builder, state, tdesc, source, dynamicShape, dynamicStrides,
256 staticShapeAttr, staticStridesAttr);
257}
258
259LogicalResult CreateNdDescOp::verify() {
260 auto srcMemrefTy = dyn_cast<MemRefType>(getSourceType());
261 size_t rank = srcMemrefTy ? srcMemrefTy.getRank() : getMixedSizes().size();
262 bool invalidElemTy = false;
263
264 // Memory space of created TensorDesc should match with the source.
265 // Both source and TensorDesc are considered for global memory by default,
266 // if the memory scope attr is not specified. If source is an integer,
267 // it is considered as ptr to global memory.
268 auto srcMemorySpace = getSourceMemorySpace();
269 auto tdescMemorySpace = static_cast<unsigned>(getType().getMemorySpace());
270 if (srcMemorySpace != tdescMemorySpace)
271 return emitOpError("Memory space mismatch.")
272 << " Source: " << srcMemorySpace
273 << ", TensorDesc: " << tdescMemorySpace;
274
275 // check source type matches the rank if it is a memref.
276 // It also should have the same ElementType as TensorDesc.
277 if (auto memrefTy = dyn_cast<MemRefType>(getSourceType()))
278 invalidElemTy |= memrefTy.getElementType() != getElementType();
279
280 bool hasExplicitShapeStrides =
281 !getShape().empty() || !getStrides().empty() ||
282 (getConstShapeAttr() && !getConstShapeAttr().empty()) ||
283 (getConstStridesAttr() && !getConstStridesAttr().empty());
284
285 if (llvm::isa<IntegerType>(getSourceType())) {
286 // strides and shape must present for integer source.
287 if (getMixedStrides().empty() || getMixedSizes().empty())
288 return emitOpError("expecting strides and shape to be present for "
289 "integer source.");
290 if (getMixedSizes().size() != getMixedStrides().size())
291 return emitOpError("Expecting the rank of shape and strides to match.");
292 } else if (srcMemrefTy && hasExplicitShapeStrides) {
293 return emitOpError("shape and strides should not be specified for a memref "
294 "source; they are inferred from the memref.");
295 }
296
297 // check result TensorDesc rank
298 if (getType().getRank() > (int64_t)rank)
299 return emitOpError("Expecting the TensorDesc rank is not greater than the "
300 "ranks of shape, strides or the memref source.");
301
302 if (invalidElemTy)
303 return emitOpError("TensorDesc should have the same element "
304 "type with the source if it is a memref.\n");
305
306 return success();
307}
308
309//===----------------------------------------------------------------------===//
310// XeGPU_PrefetchNdOp
311//===----------------------------------------------------------------------===//
312
313void PrefetchNdOp::build(OpBuilder &builder, OperationState &state,
314 Value tensorDesc, ArrayRef<OpFoldResult> offsets,
315 xegpu::CachePolicyAttr l1_hint,
316 xegpu::CachePolicyAttr l2_hint,
317 xegpu::CachePolicyAttr l3_hint,
318 xegpu::DistributeLayoutAttr layout) {
319 SmallVector<Value> dynamicOffsets;
320 SmallVector<int64_t> staticOffsets;
321 dispatchIndexOpFoldResults(offsets, dynamicOffsets, staticOffsets);
322
323 auto staticOffsetsAttr = builder.getDenseI64ArrayAttr(staticOffsets);
324
325 build(builder, state, tensorDesc, dynamicOffsets, staticOffsetsAttr, l1_hint,
326 l2_hint, l3_hint, /*anchor_layout=*/layout);
327}
328
329LogicalResult PrefetchNdOp::verify() {
330 auto tdescTy = getTensorDescType();
331
332 if (!isReadHintOrNone(getL1HintAttr()))
333 return emitOpError("invalid l1_hint: ") << getL1HintAttr();
334
335 if (!isReadHintOrNone(getL2HintAttr()))
336 return emitOpError("invalid l2_hint: ") << getL2HintAttr();
337
338 if (!isReadHintOrNone(getL3HintAttr()))
339 return emitOpError("invalid l3_hint: ") << getL3HintAttr();
340
341 int64_t tDescRank = tdescTy.getRank();
342 int64_t offsetSize = getMixedOffsets().size();
343 if (offsetSize != tDescRank)
344 return emitOpError(
345 "Mismatched ranks between offsets and tensor descriptor");
346
347 if (auto layout = getAnchorLayout()) {
348 if (!layout.isDistributable(getShapeOf(tdescTy)))
349 return emitOpError(
350 "TensorDesc shape is not distributable with the layout");
351 }
352
353 return success();
354}
355
356//===----------------------------------------------------------------------===//
357// XeGPU_LoadNdOp
358//===----------------------------------------------------------------------===//
359
360void LoadNdOp::build(OpBuilder &builder, OperationState &state, Type retType,
361 Value tensorDesc, ArrayRef<OpFoldResult> offsets,
362 UnitAttr packed, DenseI64ArrayAttr transpose,
363 xegpu::CachePolicyAttr l1_hint,
364 xegpu::CachePolicyAttr l2_hint,
365 xegpu::CachePolicyAttr l3_hint,
366 xegpu::DistributeLayoutAttr layout) {
367 SmallVector<Value> dynamicOffsets;
368 SmallVector<int64_t> staticOffsets;
369 dispatchIndexOpFoldResults(offsets, dynamicOffsets, staticOffsets);
370
371 auto staticOffsetsAttr = builder.getDenseI64ArrayAttr(staticOffsets);
372
373 build(builder, state, retType, tensorDesc, dynamicOffsets, staticOffsetsAttr,
374 packed, transpose, l1_hint, l2_hint, l3_hint,
375 /*anchor_layout=*/layout);
376}
377
378LogicalResult LoadNdOp::verify() {
379 auto tdescTy = getTensorDescType();
380 auto valueTy = getType();
381
382 if (!valueTy)
383 return emitOpError("Invalid result, it should be a VectorType.\n");
384
385 if (!isReadHintOrNone(getL1HintAttr()))
386 return emitOpError("invalid l1_hint: ") << getL1HintAttr();
387
388 if (!isReadHintOrNone(getL2HintAttr()))
389 return emitOpError("invalid l2_hint: ") << getL2HintAttr();
390
391 if (!isReadHintOrNone(getL3HintAttr()))
392 return emitOpError("invalid l3_hint: ") << getL3HintAttr();
393
394 int tdescElems = tdescTy.getNumElements() * tdescTy.getArrayLength();
395 int valueElems = valueTy.getNumElements();
396
397 // If the result vector is 1D and has less elements than the tensor
398 // descriptor, it is supposed to be a SIMT op. The layout attribute in
399 // tensor_desc is not needed.
400 if (valueElems < tdescElems && valueTy.getRank() == 1) {
401 // SIMT mode doesn't need LayoutAttr.
402 if (tdescTy.getLayoutAttr())
403 return emitOpError()
404 << "TensorDesc doesn't need LayoutAttr for SIMT code";
405
406 // For SIMT code, the load is evenly distributed across all lanes in a
407 // subgroup. Since subgroup size is arch dependent, we only check even
408 // distribution here.
409 if (tdescElems % valueElems)
410 return emitOpError()
411 << "Result shape " << makeString(getShapeOf(valueTy))
412 << " is not a valid distribution for tensor descriptor "
413 << tdescTy;
414
415 return success();
416 }
417
418 // Check SIMD mode.
419 auto tdescShape = getShapeOf(tdescTy);
420 auto valueShape = getShapeOf(valueTy);
421
422 if (getTranspose()) {
423 auto trans = getTranspose().value();
424 // Make sure the transpose value is valid, and apply it
425 if (llvm::all_of(trans, [&](size_t s) { return s < tdescShape.size(); }))
426 tdescShape = applyPermutation(tdescShape, trans);
427 else
428 mlir::emitWarning(getLoc()) << "Invalid transpose attr. It is ignored.";
429 }
430
431 if (getPacked()) {
432 if (tdescTy.getRank() == 2) {
433 const int axis = 0;
434 auto vnni_factor = valueShape.back();
435 tdescShape[axis] /= vnni_factor;
436 tdescShape.push_back(vnni_factor);
437 } else {
438 mlir::emitWarning(getLoc())
439 << "Invalid Packed Attr. It is ignored (available for 2D "
440 "TensorDesc only).";
441 }
442 }
443
444 // Handle array_length. Two result shape conventions are accepted:
445 // * 3D shape: leading array_length dimension prepended, e.g. descriptor
446 // 16x16 with array_length=2 -> [2, 16, 16].
447 // * Stacked 2D shape: array blocks stacked along the non-FCD (first)
448 // dimension, e.g. descriptor 16x16 with array_length=2 -> [32, 16].
449 auto array_len = tdescTy.getArrayLength();
450 SmallVector<int64_t> stacked2DShape(tdescShape);
451 SmallVector<int64_t> threeDShape(tdescShape);
452 if (array_len > 1 && !tdescShape.empty()) {
453 stacked2DShape[0] *= array_len;
454 threeDShape.insert(threeDShape.begin(), array_len);
455 }
456
457 if (valueShape != stacked2DShape && valueShape != threeDShape)
458 return emitOpError() << "Result shape " << makeString(valueShape)
459 << " is not consistent with tensor descriptor "
460 << tdescTy;
461
462 int64_t tDescRank = tdescTy.getRank();
463 int64_t offsetSize = getMixedOffsets().size();
464 if (offsetSize != tDescRank)
465 return emitOpError(
466 "Mismatched ranks between offsets and tensor descriptor");
467
468 if (auto layout = getAnchorLayout()) {
469 if (!layout.isDistributable(getShapeOf(tdescTy)))
470 return emitOpError(
471 "TensorDesc shape is not distributable with the layout");
472 }
473
474 return success();
475}
476
477//===----------------------------------------------------------------------===//
478// XeGPU_StoreNdOp
479//===----------------------------------------------------------------------===//
480
481void StoreNdOp::build(OpBuilder &builder, OperationState &state, Value value,
482 Value tensorDesc, ArrayRef<OpFoldResult> offsets,
483 xegpu::CachePolicyAttr l1_hint,
484 xegpu::CachePolicyAttr l2_hint,
485 xegpu::CachePolicyAttr l3_hint,
486 xegpu::DistributeLayoutAttr layout) {
487 SmallVector<Value> dynamicOffsets;
488 SmallVector<int64_t> staticOffsets;
489 dispatchIndexOpFoldResults(offsets, dynamicOffsets, staticOffsets);
490
491 auto staticOffsetsAttr = builder.getDenseI64ArrayAttr(staticOffsets);
492
493 build(builder, state, value, tensorDesc, dynamicOffsets, staticOffsetsAttr,
494 l1_hint, l2_hint, l3_hint, /*anchor_layout=*/layout);
495}
496
497LogicalResult StoreNdOp::verify() {
498 auto dstTy = getTensorDescType(); // Tile
499 auto valTy = getValueType(); // Vector
500
501 if (!valTy)
502 return emitOpError("Expecting a VectorType result.\n");
503
504 if (!isWriteHintOrNone(getL1HintAttr()))
505 return emitOpError("invalid l1_hint: ") << getL1HintAttr();
506
507 if (!isWriteHintOrNone(getL2HintAttr()))
508 return emitOpError("invalid l2_hint: ") << getL2HintAttr();
509
510 if (!isWriteHintOrNone(getL3HintAttr()))
511 return emitOpError("invalid l3_hint: ") << getL3HintAttr();
512
513 auto array_len = dstTy.getArrayLength();
514 if (array_len > 1)
515 return emitOpError("array length is not supported by store_nd.\n");
516
517 auto tdescElems = dstTy.getNumElements();
518 auto valueElems = valTy.getNumElements();
519
520 // Similar to LoadNdOp, if the value vector is 1D and has less elements than
521 // the tensor descriptor, it is supposed to be a SIMT op. The layout attribute
522 // in tensor_desc is not needed.
523 if (valTy.getRank() == 1 && valueElems < tdescElems) {
524 // SIMT mode doesn't need LayoutAttr.
525 if (dstTy.getLayoutAttr())
526 return emitOpError()
527 << "TensorDesc doesn't need LayoutAttr for SIMT code";
528
529 if (tdescElems % valueElems)
530 return emitOpError()
531 << "Value shape " << makeString(getShapeOf(valTy))
532 << " is not a valid distribution for tensor descriptor " << dstTy;
533
534 return success();
535 }
536
537 // SIMD code should have the same shape as the tensor descriptor.
538 auto tdescShape = getShapeOf(dstTy);
539 auto valueShape = getShapeOf(valTy);
540 if (tdescShape != valueShape)
541 return emitOpError() << "Value shape " << makeString(valueShape)
542 << " is not consistent with tensor descriptor "
543 << dstTy;
544
545 int64_t tDescRank = dstTy.getRank();
546 int64_t offsetSize = getMixedOffsets().size();
547 if (offsetSize != tDescRank)
548 return emitOpError(
549 "Mismatched ranks between offsets and tensor descriptor");
550
551 if (auto layout = getAnchorLayout()) {
552 if (!layout.isDistributable(tdescShape))
553 return emitOpError(
554 "TensorDesc shape is not distributable with the layout");
555 }
556
557 return success();
558}
559
560//===----------------------------------------------------------------------===//
561// XeGPU_PrefetchOp
562//===----------------------------------------------------------------------===//
563LogicalResult PrefetchOp::verify() {
564 if (!isReadHintOrNone(getL1HintAttr()))
565 return emitOpError("invalid l1_hint: ") << getL1HintAttr();
566
567 if (!isReadHintOrNone(getL2HintAttr()))
568 return emitOpError("invalid l2_hint: ") << getL2HintAttr();
569
570 if (!isReadHintOrNone(getL3HintAttr()))
571 return emitOpError("invalid l3_hint: ") << getL3HintAttr();
572
573 auto srcTy = getSourceType();
574 if (srcTy.isInteger() && !getOffsetAlignByteAttr())
575 return emitOpError("offset_align_byte is required with integer source.");
576
577 if (getOffsetAlignByteAttr() && !srcTy.isInteger())
578 return emitOpError("offset_align_byte only allowed with integer source.");
579
580 if (auto layout = getAnchorLayout()) {
581 // get the offset operand and its shape
582 auto offsetsTy = getOffsets().getType();
583 if (llvm::isa<VectorType>(offsetsTy) &&
584 !layout.isDistributable(getShapeOf(offsetsTy)))
585 return emitOpError("offset shape is not distributable with the layout");
586 }
587
588 return success();
589}
590
591//===----------------------------------------------------------------------===//
592// XeGPU_LoadGatherOp
593//===----------------------------------------------------------------------===//
594LogicalResult LoadGatherOp::verify() {
595 auto maskTy = getMaskType();
596 auto valueTy = getValueType();
597
598 if (!isReadHintOrNone(getL1HintAttr()))
599 return emitOpError("invalid l1_hint: ") << getL1HintAttr();
600
601 if (!isReadHintOrNone(getL2HintAttr()))
602 return emitOpError("invalid l2_hint: ") << getL2HintAttr();
603
604 if (!isReadHintOrNone(getL3HintAttr()))
605 return emitOpError("invalid l3_hint: ") << getL3HintAttr();
606
607 auto srcTy = getSourceType();
608 uint64_t chunkSize = static_cast<int64_t>(getChunkSize().value_or(1));
609 auto memTy = dyn_cast<MemRefType>(srcTy);
610
611 if (memTy && (getElementType() != memTy.getElementType()))
612 return emitError() << "Value should have the same element type as MemRef.";
613
614 if (auto layout = getAnchorLayout()) {
615 if (!layout.isDistributable(getShapeOf(valueTy)))
616 return emitOpError("Value shape is not distributable with the layout");
617 }
618
619 auto offsetsTy = getOffsets().getType();
620 if (failed(isValidContiguity(getContiguity(), offsetsTy,
621 [&]() { return emitOpError(); })))
622 return failure();
623 return isValidGatherScatterBufferParams(offsetsTy, maskTy, valueTy, chunkSize,
624 [&]() { return emitOpError(); });
625}
626
627void LoadGatherOp::build(OpBuilder &builder, OperationState &state,
628 Type valueType, Value source,
629 ArrayRef<OpFoldResult> offsets, Value mask,
630 IntegerAttr chunk_size, xegpu::CachePolicyAttr l1_hint,
631 xegpu::CachePolicyAttr l2_hint,
632 xegpu::CachePolicyAttr l3_hint) {
633 auto loc = source.getLoc();
634 int64_t size = static_cast<int64_t>(offsets.size());
635 auto type = VectorType::get(size, builder.getIndexType());
636 auto values = getValueOrCreateConstantIndexOp(builder, loc, offsets);
637 auto offset = vector::FromElementsOp::create(builder, loc, type, values);
638
639 build(builder, state, valueType, source, offset, mask, chunk_size, l1_hint,
640 l2_hint, l3_hint, /*anchor_layout=*/nullptr,
641 /*contiguity=*/nullptr);
642}
643
644void LoadGatherOp::build(OpBuilder &builder, OperationState &state,
645 Type valueType, Value source,
646 ArrayRef<OpFoldResult> offsets, Value mask,
647 IntegerAttr chunk_size, xegpu::CachePolicyAttr l1_hint,
648 xegpu::CachePolicyAttr l2_hint,
649 xegpu::CachePolicyAttr l3_hint,
650 DistributeLayoutAttr layout) {
651 auto loc = source.getLoc();
652 int64_t size = static_cast<int64_t>(offsets.size());
653 auto type = VectorType::get(size, builder.getIndexType());
654 auto values = getValueOrCreateConstantIndexOp(builder, loc, offsets);
655 auto offset = vector::FromElementsOp::create(builder, loc, type, values);
656
657 build(builder, state, valueType, source, offset, mask, chunk_size, l1_hint,
658 l2_hint, l3_hint, layout, /*contiguity=*/nullptr);
659}
660
661//===----------------------------------------------------------------------===//
662// XeGPU_StoreScatterOp
663//===----------------------------------------------------------------------===//
664LogicalResult StoreScatterOp::verify() {
665 auto maskTy = getMaskType();
666 auto valueTy = getValueType();
667
668 if (!isWriteHintOrNone(getL1HintAttr()))
669 return emitOpError("invalid l1_hint: ") << getL1HintAttr();
670
671 if (!isWriteHintOrNone(getL2HintAttr()))
672 return emitOpError("invalid l2_hint: ") << getL2HintAttr();
673
674 if (!isWriteHintOrNone(getL3HintAttr()))
675 return emitOpError("invalid l3_hint: ") << getL3HintAttr();
676
677 auto destTy = getDestType();
678 uint64_t chunkSize = static_cast<int64_t>(getChunkSize().value_or(1));
679 auto memTy = dyn_cast<MemRefType>(destTy);
680
681 if (memTy && (getElementType() != memTy.getElementType()))
682 return emitError() << "Value should have the same element type as MemRef.";
683
684 if (auto layout = getAnchorLayout()) {
685 if (!layout.isDistributable(getShapeOf(valueTy)))
686 return emitOpError("Value shape is not distributable with the layout");
687 }
688
689 auto offsetsTy = getOffsets().getType();
690 if (failed(isValidContiguity(getContiguity(), offsetsTy,
691 [&]() { return emitOpError(); })))
692 return failure();
693 return isValidGatherScatterBufferParams(offsetsTy, maskTy, valueTy, chunkSize,
694 [&]() { return emitOpError(); });
695}
696
697void StoreScatterOp::build(OpBuilder &builder, OperationState &state,
698 Value value, Value dest,
699 ArrayRef<OpFoldResult> offsets, Value mask,
700 IntegerAttr chunk_size,
701 xegpu::CachePolicyAttr l1_hint,
702 xegpu::CachePolicyAttr l2_hint,
703 xegpu::CachePolicyAttr l3_hint) {
704 auto loc = dest.getLoc();
705 int64_t size = static_cast<int64_t>(offsets.size());
706 auto type = VectorType::get(size, builder.getIndexType());
707 auto values = getValueOrCreateConstantIndexOp(builder, loc, offsets);
708 auto offset = vector::FromElementsOp::create(builder, loc, type, values);
709
710 // Call the correct builder overload that does not expect result types.
711 build(builder, state, value, dest, offset, mask, chunk_size, l1_hint, l2_hint,
712 l3_hint, /*anchor_layout=*/nullptr, /*contiguity=*/nullptr);
713}
714
715void StoreScatterOp::build(
716 OpBuilder &builder, OperationState &state, Value value, Value dest,
717 ArrayRef<OpFoldResult> offsets, Value mask, IntegerAttr chunk_size,
718 xegpu::CachePolicyAttr l1_hint, xegpu::CachePolicyAttr l2_hint,
719 xegpu::CachePolicyAttr l3_hint, DistributeLayoutAttr layout) {
720 auto loc = dest.getLoc();
721 int64_t size = static_cast<int64_t>(offsets.size());
722 auto type = VectorType::get(size, builder.getIndexType());
723 auto values = getValueOrCreateConstantIndexOp(builder, loc, offsets);
724 auto offset = vector::FromElementsOp::create(builder, loc, type, values);
725
726 // Call the correct builder overload that does not expect result types.
727 build(builder, state, value, dest, offset, mask, chunk_size, l1_hint, l2_hint,
728 l3_hint, layout, /*contiguity=*/nullptr);
729}
730
731//===----------------------------------------------------------------------===//
732// DPAS Common Verification Helpers
733//===----------------------------------------------------------------------===//
734
735// Helper to verify layout distributability for a value
736static LogicalResult
738 std::optional<DistributeLayoutAttr> layout,
739 ArrayRef<int64_t> shape, StringRef operandName) {
740 if (layout && !layout->isDistributable(
741 SmallVector<int64_t>(shape.begin(), shape.end())))
742 return op->emitOpError(operandName)
743 << " shape is not distributable with the layout";
744 return success();
745}
746
747// Helper to verify M, N, K dimensions match between A, B, and result matrices
748static LogicalResult verifyDpasDimensions(Operation *op,
749 ArrayRef<int64_t> aShape,
750 ArrayRef<int64_t> bShape,
751 ArrayRef<int64_t> resShape) {
752
753 auto aRank = aShape.size();
754 auto bRank = bShape.size();
755 auto resRank = resShape.size();
756 if (aRank == 1 && bRank == 1 && resRank == 1)
757 return success();
758
759 // A must be at least 2D, B must be 2D or 3D (innermost dims), result at
760 // least 2D.
761 if (aRank < 2)
762 return op->emitOpError("A operand must be at least a 2D vector.");
763 if (bRank < 2)
764 return op->emitOpError("B operand must be at least a 2D vector.");
765 if (resRank < 2)
766 return op->emitOpError("Result must be at least a 2D vector.");
767
768 // FIXME: B may have one extra trailing dim for VNNI packing
769 // (B[batch..., K/vnni, N, vnni]). We plan to drop VNNI packing support, so
770 // rather than properly verifying the packed dimensions, we simply accept
771 // the packed form here and skip the detailed verification. This branch
772 // should be removed once VNNI packing support is dropped.
773 if (bRank == aRank + 1)
774 return success();
775
776 // All operands have the same rank. They share the same batch dimensions,
777 // with the last two dims being the core matmul dims: A[batch..., M, K],
778 // B[batch..., K, N], result[batch..., M, N].
779 if (aRank != bRank || aRank != resRank)
780 return op->emitOpError("Rank mismatch among A, B, and result.");
781
782 int64_t batchRank = aRank - 2;
783
784 // Verify batch dimensions match.
785 for (int64_t i = 0; i < batchRank; ++i) {
786 if (aShape[i] != resShape[i])
787 return op->emitOpError("Batch dimension mismatch at dim ")
788 << i << ": A has " << aShape[i] << " but result has "
789 << resShape[i] << ".";
790 if (aShape[i] != bShape[i])
791 return op->emitOpError("Batch dimension mismatch at dim ")
792 << i << ": A has " << aShape[i] << " but B has " << bShape[i]
793 << ".";
794 }
795
796 // Core matmul dimensions (last two dims of each operand).
797 int64_t aM = aShape[batchRank];
798 int64_t aK = aShape[batchRank + 1];
799 int64_t bK = bShape[batchRank];
800 int64_t bN = bShape[batchRank + 1];
801 int64_t resM = resShape[batchRank];
802 int64_t resN = resShape[batchRank + 1];
803
804 // Verify K dimension match between A and B
805 if (bK != aK)
806 return op->emitOpError("K-dimension mismatch: A has K=")
807 << aK << " but B has K=" << bK << ".";
808
809 // Verify M dimension match between A and result
810 if (aM != resM)
811 return op->emitOpError("M-dimension mismatch: A has M=")
812 << aM << " but result has M=" << resM << ".";
813
814 // Verify N dimension match between B and result
815 if (bN != resN)
816 return op->emitOpError("N-dimension mismatch: B has N=")
817 << bN << " but result has N=" << resN << ".";
818
819 return success();
820}
821
822// Helper to verify accumulator matches result type
823static LogicalResult verifyDpasAccumulator(Operation *op, Type accType,
824 Type resultType) {
825 if (accType != resultType)
826 return op->emitOpError("Accumulator type must match result type.");
827 return success();
828}
829
830//===----------------------------------------------------------------------===//
831// XeGPU_DpasOp
832//===----------------------------------------------------------------------===//
833LogicalResult DpasOp::verify() {
834 auto lhsShape = getLhsType().getShape();
835 auto rhsShape = getRhsType().getShape();
836 auto resShape = getResultType().getShape();
837
838 // Verify layout distributability
839 if (failed(
840 verifyLayoutDistributable(*this, getLayoutCd(), resShape, "Result")))
841 return failure();
842 if (failed(verifyLayoutDistributable(*this, getLayoutA(), lhsShape, "A")))
843 return failure();
844 if (failed(verifyLayoutDistributable(*this, getLayoutB(), rhsShape, "B")))
845 return failure();
846
847 // Verify accumulator if present
848 if (getAcc() &&
849 failed(verifyDpasAccumulator(*this, getAcc().getType(), getResultType())))
850 return failure();
851
852 return verifyDpasDimensions(*this, lhsShape, rhsShape, resShape);
853}
854
855//===----------------------------------------------------------------------===//
856// XeGPU_ConvertLayoutOp
857//===----------------------------------------------------------------------===//
858LogicalResult ConvertLayoutOp::verify() {
859 auto resLayout = getTargetLayout();
860 if (!resLayout)
861 return emitOpError("expected target layout.");
862 auto srcLayout = getEffectiveInputLayout();
863
864 // both input and target layouts should be WgLayout or SgLayout at the same
865 // time.
866 if ((!srcLayout.isForWorkgroup() || !resLayout.isForWorkgroup()) &&
867 (!srcLayout.isForSubgroup() || !resLayout.isForSubgroup()))
868 return emitOpError("expected input layout and target layout be WgLayout or "
869 "SgLayout at the same time.");
870
871 Type srcType = getSource().getType();
872 if (llvm::isa<VectorType>(srcType)) {
873 SmallVector<int64_t> shape(llvm::cast<VectorType>(srcType).getShape());
874 if (!srcLayout.isDistributable(shape))
875 return emitOpError(
876 "invalid input layout, data cannot be evenly distributed.");
877
878 if (!resLayout.isDistributable(shape))
879 return emitOpError(
880 "invalid target layout, data cannot be evenly distributed.");
881 }
882 return mlir::success();
883}
884
885//===----------------------------------------------------------------------===//
886// XeGPU_LoadMatrixOp
887//===----------------------------------------------------------------------===//
888void LoadMatrixOp::build(OpBuilder &builder, OperationState &state, Type res,
891 DistributeLayoutAttr layout) {
892 llvm::SmallVector<Value> dynamicOffsets;
893 llvm::SmallVector<int64_t> staticOffsets;
894 dispatchIndexOpFoldResults(offsets, dynamicOffsets, staticOffsets);
895 auto staticOffsetsAttr = builder.getDenseI64ArrayAttr(staticOffsets);
896 // Call the generated builder with all parameters (including optional ones as
897 // nullptr/empty)
898 build(builder, state, res, memDesc, dynamicOffsets, staticOffsetsAttr,
899 /*subgroup_block_io=*/nullptr, layout);
900}
901
902LogicalResult LoadMatrixOp::verify() {
903
904 auto resTy = dyn_cast<VectorType>(getRes().getType());
905 UnitAttr subgroup_block_io = getSubgroupBlockIoAttr();
906 MemDescType mdescTy = getMemDesc().getType();
907
908 return IsValidMatrixOpParams(resTy, mdescTy, subgroup_block_io,
909 getLayoutAttr(), [&]() { return emitError(); });
910}
911
912//===----------------------------------------------------------------------===//
913// XeGPU_StoreMatrixOp
914//===----------------------------------------------------------------------===//
915void StoreMatrixOp::build(OpBuilder &builder, OperationState &state, Value data,
918 DistributeLayoutAttr layout) {
919 llvm::SmallVector<Value> dynamicOffsets;
920 llvm::SmallVector<int64_t> staticOffsets;
921 dispatchIndexOpFoldResults(offsets, dynamicOffsets, staticOffsets);
922 auto staticOffsetsAttr = builder.getDenseI64ArrayAttr(staticOffsets);
923 build(builder, state, data, memDesc, dynamicOffsets, staticOffsetsAttr,
924 /*subgroup_block_io=*/nullptr, layout);
925}
926
927LogicalResult StoreMatrixOp::verify() {
928
929 auto dataTy = dyn_cast<VectorType>(getData().getType());
930 UnitAttr subgroup_block_io = getSubgroupBlockIoAttr();
931 MemDescType mdescTy = getMemDesc().getType();
932 return IsValidMatrixOpParams(dataTy, mdescTy, subgroup_block_io,
933 getLayoutAttr(), [&]() { return emitError(); });
934}
935
936//===----------------------------------------------------------------------===//
937// XeGPU_TruncfOp
938//===----------------------------------------------------------------------===//
939
940LogicalResult TruncfOp::verify() {
941 auto sourceVecType = dyn_cast<VectorType>(getSource().getType());
942 auto resultVecType = dyn_cast<VectorType>(getResult().getType());
943
944 if (sourceVecType.getElementTypeBitWidth() <=
945 resultVecType.getElementTypeBitWidth())
946 return emitOpError("input type must be wider than result type.");
947
948 return success();
949}
950
951//===----------------------------------------------------------------------===//
952// XeGPU_LaneShuffleOp
953//===----------------------------------------------------------------------===//
954
955LogicalResult LaneShuffleOp::verify() {
956 // With a single element per lane there is nothing to re-distribute, so the
957 // operation would be a no-op.
958 if (getSourceType().getNumElements() < 2)
959 return emitOpError("requires a source vector with at least 2 elements.");
960
961 return success();
962}
963
964OpFoldResult LaneShuffleOp::fold(FoldAdaptor adaptor) {
965 // The two modes are exact inverses, so a pack feeding an unpack (or vice
966 // versa) restores the original fragments.
967 auto producer = getSource().getDefiningOp<LaneShuffleOp>();
968 if (producer && producer.getMode() != getMode())
969 return producer.getSource();
970
971 return {};
972}
973
974//===----------------------------------------------------------------------===//
975// XeGPU_DpasMxOp
976//===----------------------------------------------------------------------===//
977
978LogicalResult DpasMxOp::verify() {
979 auto aShape = getAType().getShape();
980 auto bShape = getBType().getShape();
981 auto resShape = getResultType().getShape();
982
983 // Verify layout distributability for A, B, and result
984 if (failed(
985 verifyLayoutDistributable(*this, getLayoutCd(), resShape, "Result")))
986 return failure();
987 if (failed(verifyLayoutDistributable(*this, getLayoutA(), aShape, "A")))
988 return failure();
989 if (failed(verifyLayoutDistributable(*this, getLayoutB(), bShape, "B")))
990 return failure();
991
992 // Verify accumulator if present
993 if (getAcc() &&
994 failed(verifyDpasAccumulator(*this, getAcc().getType(), getResultType())))
995 return failure();
996
997 // Verify M, N, K dimensions
998 if (failed(verifyDpasDimensions(*this, aShape, bShape, resShape)))
999 return failure();
1000
1001 // Determine batch rank from A operand.
1002 int64_t aBatchRank = aShape.size() - 2;
1003
1004 // Validate scale_a if present
1005 if (getScaleA()) {
1006 auto scaleAVecType = dyn_cast<VectorType>(getScaleAType());
1007 // Only validate if scale is a vector (scalars are always valid)
1008 if (scaleAVecType && scaleAVecType.getRank() > 1) {
1009 auto scaleAShape = scaleAVecType.getShape();
1010
1011 if (scaleAVecType.getRank() < 2)
1012 return emitOpError("Scale A must be at least a 2D vector when not a "
1013 "scalar.");
1014
1015 // Verify layout distributability for scale_a
1016 if (failed(verifyLayoutDistributable(*this, getLayoutAScale(),
1017 scaleAShape, "ScaleA")))
1018 return failure();
1019
1020 // Validate M dimension: scale_a's M must match A's M (last-1 dim)
1021 if (scaleAShape[scaleAShape.size() - 2] != aShape[aBatchRank])
1022 return emitOpError("Scale A M dimension [")
1023 << scaleAShape[scaleAShape.size() - 2]
1024 << "] must match A M dimension [" << aShape[aBatchRank] << "].";
1025 }
1026 }
1027
1028 // Validate scale_b if present
1029 if (getScaleB()) {
1030 auto scaleBVecType = dyn_cast<VectorType>(getScaleBType());
1031 // Only validate if scale is a vector (scalars are always valid)
1032 if (scaleBVecType && scaleBVecType.getRank() > 1) {
1033 auto scaleBShape = scaleBVecType.getShape();
1034
1035 if (scaleBVecType.getRank() < 2)
1036 return emitOpError("Scale B must be at least a 2D vector when not a "
1037 "scalar.");
1038
1039 // Verify layout distributability for scale_b
1040 if (failed(verifyLayoutDistributable(*this, getLayoutBScale(),
1041 scaleBShape, "ScaleB")))
1042 return failure();
1043
1044 // Validate N dimension: scale_b's N (last dim) must match B's N (last
1045 // dim)
1046 if (scaleBShape.back() != bShape.back())
1047 return emitOpError("Scale B N dimension [")
1048 << scaleBShape.back() << "] must match B N dimension ["
1049 << bShape.back() << "].";
1050 }
1051 }
1052
1053 // Validate scale K dimension compatibility if both scales are present and
1054 // vectors
1055 if (getScaleA() && getScaleB()) {
1056 auto scaleAVecType = dyn_cast<VectorType>(getScaleAType());
1057 auto scaleBVecType = dyn_cast<VectorType>(getScaleBType());
1058
1059 if (scaleAVecType && scaleBVecType && scaleAVecType.getRank() > 1 &&
1060 scaleBVecType.getRank() > 1) {
1061 auto scaleAShape = scaleAVecType.getShape();
1062 auto scaleBShape = scaleBVecType.getShape();
1063
1064 // Validate scale K dimension compatibility: scale_a's last dim must
1065 // match scale_b's second-to-last dim
1066 if (scaleAShape.back() != scaleBShape[scaleBShape.size() - 2])
1067 return emitOpError("Scale K dimension mismatch: scale_a has K=")
1068 << scaleAShape.back()
1069 << " but scale_b has K=" << scaleBShape[scaleBShape.size() - 2]
1070 << ".";
1071 }
1072 }
1073
1074 return success();
1075}
1076
1077namespace mlir {
1078#include <mlir/Dialect/XeGPU/IR/XeGPUAttrInterface.cpp.inc>
1079} // namespace mlir
1080#include <mlir/Dialect/XeGPU/IR/XeGPUEnums.cpp.inc>
1081#define GET_OP_CLASSES
1082#include <mlir/Dialect/XeGPU/IR/XeGPU.cpp.inc>
return success()
p<< " : "<< getMemRefType()<< ", "<< getType();}static LogicalResult verifyVectorMemoryOp(Operation *op, MemRefType memrefType, VectorType vectorType) { if(memrefType.getElementType() !=vectorType.getElementType()) return op-> emitOpError("requires memref and vector types of the same elemental type")
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
static Type getElementType(Type type)
Determine the element type of type.
static int64_t getNumElements(Type t)
Compute the total number of elements in the given type, also taking into account nested types.
ArrayAttr()
static Type getValueType(Attribute attr)
Definition SPIRVOps.cpp:835
static ArrayRef< int64_t > getShape(Type type)
Returns the shape of the given type.
Definition Traits.cpp:117
static SmallVector< int64_t > getShapeOf(Type type)
Definition XeGPUOps.cpp:40
static LogicalResult verifyDpasAccumulator(Operation *op, Type accType, Type resultType)
Definition XeGPUOps.cpp:823
LogicalResult IsValidMatrixOpParams(VectorType dataTy, MemDescType mdescTy, UnitAttr subgroup_block_io, DistributeLayoutAttr layout, function_ref< InFlightDiagnostic()> emitError)
Definition XeGPUOps.cpp:140
static std::string makeString(T array, bool breakline=false)
Definition XeGPUOps.cpp:26
static bool isWriteHintOrNone(const CachePolicyAttr &attr)
Definition XeGPUOps.cpp:57
static bool isReadHintOrNone(const CachePolicyAttr &attr)
Definition XeGPUOps.cpp:49
static LogicalResult isValidGatherScatterBufferParams(Type offsetsTy, Type maskTy, VectorType valueTy, int64_t chunkSize, function_ref< InFlightDiagnostic()> emitError)
Definition XeGPUOps.cpp:66
static LogicalResult isValidContiguity(std::optional< uint64_t > contiguity, Type offsetsTy, function_ref< InFlightDiagnostic()> emitError)
Definition XeGPUOps.cpp:121
static LogicalResult verifyDpasDimensions(Operation *op, ArrayRef< int64_t > aShape, ArrayRef< int64_t > bShape, ArrayRef< int64_t > resShape)
Definition XeGPUOps.cpp:748
static LogicalResult verifyLayoutDistributable(Operation *op, std::optional< DistributeLayoutAttr > layout, ArrayRef< int64_t > shape, StringRef operandName)
Definition XeGPUOps.cpp:737
Attributes are known-constant values of operations.
Definition Attributes.h:25
DenseI64ArrayAttr getDenseI64ArrayAttr(ArrayRef< int64_t > values)
Definition Builders.cpp:175
IndexType getIndexType()
Definition Builders.cpp:59
This class represents a diagnostic that is inflight and set to be reported.
This class helps build Operations.
Definition Builders.h:210
This class represents a single result from folding an operation.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isInteger() const
Return true if this is an integer type (with the specified width).
Definition Types.cpp:58
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
Location getLoc() const
Return the location of this value.
Definition Value.cpp:24
bool isStaticShapeAndContiguousRowMajor(MemRefType type)
Returns true, if the memref type has static shapes and represents a contiguous chunk of memory.
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given memref value.
Definition MemRefOps.cpp:79
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
Include the generated interface declarations.
InFlightDiagnostic emitWarning(Location loc)
Utility method to emit a warning message using this location.
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:307
SmallVector< T > applyPermutation(ArrayRef< T > input, ArrayRef< int64_t > permutation)
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
std::conditional_t< std::is_same_v< Ty, mlir::Type >, mlir::Value, detail::TypedValue< Ty > > TypedValue
If Ty is mlir::Type this will select Value instead of having a wrapper around it.
Definition Value.h:494
void dispatchIndexOpFoldResults(ArrayRef< OpFoldResult > ofrs, SmallVectorImpl< Value > &dynamicVec, SmallVectorImpl< int64_t > &staticVec)
Helper function to dispatch multiple OpFoldResults according to the behavior of dispatchIndexOpFoldRe...
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
Definition Utils.cpp:114
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147
This represents an operation in an abstracted form, suitable for use with the builder APIs.