17#include "llvm/Support/Debug.h"
19#define DEBUG_TYPE "xegpu"
25static std::string
makeString(T array,
bool breakline =
false) {
28 llvm::raw_string_ostream os(buf);
30 for (
size_t i = 1; i < array.size(); i++) {
31 os << array[i - 1] <<
", ";
35 os << array.back() <<
"]";
41 if (
auto ty = llvm::dyn_cast<ShapedType>(type))
51 auto kind = attr.getValue();
52 return kind == CachePolicy::CACHED || kind == CachePolicy::UNCACHED ||
53 kind == CachePolicy::STREAMING || kind == CachePolicy::READ_INVALIDATE;
59 auto kind = attr.getValue();
60 return kind == CachePolicy::CACHED || kind == CachePolicy::UNCACHED ||
61 kind == CachePolicy::WRITE_BACK || kind == CachePolicy::WRITE_THROUGH;
66 VectorType valueTy,
int64_t chunkSize,
69 auto maskVecTy = dyn_cast<VectorType>(maskTy);
70 auto offsetsVecTy = dyn_cast<VectorType>(offsetsTy);
73 return emitError() <<
"Expecting chunk size == 1 for scalar result";
74 if (maskVecTy || offsetsVecTy)
75 return emitError() <<
"Expecting scalar mask and offsets.";
76 else if (maskVecTy && offsetsVecTy)
77 return emitError() <<
"Expecting a vector type result.";
81 auto valueSize = valueTy.getNumElements();
83 if (!maskVecTy && !offsetsVecTy) {
84 if (valueSize != chunkSize)
85 return emitError() <<
"value elements must match chunk size "
93 return emitError() <<
"Expecting a vector type mask.";
94 int64_t maskSize = maskVecTy.getNumElements();
97 if ((valueTy.getRank() == 1) && (valueSize != chunkSize))
98 return emitError() <<
"value elements must match chunk size "
101 if (valueSize != maskSize)
103 <<
"Mask should match value except the chunk size dim.";
109 expectedMaskShape.pop_back();
110 if (expectedMaskShape != maskShape)
111 return emitError() <<
"Mask should match value except the chunk size dim.";
118 UnitAttr subgroup_block_io, DistributeLayoutAttr layout,
122 if (subgroup_block_io)
123 return emitError() <<
"subgroup_block_io "
124 "are only allowed when result is a VectorType.";
133 ArrayAttr strideAttr = mdescTy.getStrideAttr();
135 for (
Attribute attr : strideAttr.getValue()) {
136 strides.push_back(cast<IntegerAttr>(attr).getInt());
138 if (subgroup_block_io && layout) {
139 auto laneData = layout.getEffectiveLaneDataAsInt();
140 auto laneLayout = layout.getEffectiveLaneLayoutAsInt();
141 if (!laneData.empty()) {
142 bool isLaneDataContiguous =
143 std::all_of(laneData.begin(), std::prev(laneData.end()),
144 [](
int x) { return x == 1; });
145 if (!isLaneDataContiguous)
146 return emitError() <<
"With subgroup_block_io, accessed data must be "
147 "contiguous and coalesced.";
148 for (
size_t i = 0; i < laneData.size(); ++i) {
149 if (laneLayout[i] != blockShape[i])
150 return emitError() <<
"With subgroup_block_io, the block shape must "
151 "match the lane layout.";
152 if (laneLayout[i] != 1 && strides[i] != 1)
153 return emitError() <<
"With subgroup_block_io, the distributed "
154 "dimensions must be contiguous.";
159 if (layout && !layout.isDistributable(
161 return emitError() <<
"Value shape is not distributable with the layout";
163 if (dataShape.size() == mdescShape.size()) {
164 if (llvm::any_of(llvm::zip_equal(dataShape, mdescShape),
165 [](
auto p) {
return std::get<0>(p) > std::get<1>(p); }))
166 return emitError() <<
"data shape must not exceed mem_desc shape.";
170 if (subgroup_block_io && !blockShape.size())
171 return emitError() <<
"mem_desc must have block attribute when "
172 "subgroup_block_io is set.";
182 [[maybe_unused]]
auto ty = source.getType();
183 assert(ty.hasStaticShape() &&
"expecting a memref with static shape");
185 build(builder, state, tdesc, source,
ValueRange({}) ,
196 assert((isa<IntegerType, MemRefType>(srcTy)) &&
197 "Source has to be either int or memref.");
211 if (
auto memrefTy = dyn_cast<MemRefType>(srcTy)) {
212 auto memrefShape = memrefTy.getShape();
213 auto [memrefStrides, _] = memrefTy.getStridesAndOffset();
218 if (staticShape == memrefShape && staticStrides == memrefStrides &&
219 dynamicShape.empty() && dynamicStrides.empty()) {
225 build(builder, state, tdesc, source, dynamicShape, dynamicStrides,
226 staticShapeAttr, staticStridesAttr);
229LogicalResult CreateNdDescOp::verify() {
231 bool invalidRank = rank != getMixedStrides().size();
232 bool invalidElemTy =
false;
238 auto srcMemorySpace = getSourceMemorySpace();
239 auto tdescMemorySpace =
static_cast<unsigned>(
getType().getMemorySpace());
240 if (srcMemorySpace != tdescMemorySpace)
242 <<
" Source: " << srcMemorySpace
243 <<
", TensorDesc: " << tdescMemorySpace;
247 if (
auto memrefTy = dyn_cast<MemRefType>(getSourceType()))
250 if (llvm::isa<IntegerType>(getSourceType())) {
253 return emitOpError(
"expecting strides and shape to be present for "
259 "Expecting the rank of shape, strides, and source (if source "
260 "is a memref) should match with each other.");
264 return emitOpError(
"Expecting the TensorDesc rank is not greater than the "
265 "ranks of shape, strides or the memref source.");
268 return emitOpError(
"TensorDesc should have the same element "
269 "type with the source if it is a memref.\n");
280 xegpu::CachePolicyAttr l1_hint,
281 xegpu::CachePolicyAttr l2_hint,
282 xegpu::CachePolicyAttr l3_hint,
283 xegpu::DistributeLayoutAttr layout) {
290 build(builder, state, tensorDesc, dynamicOffsets, staticOffsetsAttr, l1_hint,
291 l2_hint, l3_hint, layout);
294LogicalResult PrefetchNdOp::verify() {
295 auto tdescTy = getTensorDescType();
298 return emitOpError(
"invalid l1_hint: ") << getL1HintAttr();
301 return emitOpError(
"invalid l2_hint: ") << getL2HintAttr();
304 return emitOpError(
"invalid l3_hint: ") << getL3HintAttr();
306 int64_t tDescRank = tdescTy.getRank();
307 int64_t offsetSize = getMixedOffsets().size();
308 if (offsetSize != tDescRank)
310 "Mismatched ranks between offsets and tensor descriptor");
312 if (
auto layout = getAnchorLayout()) {
313 if (!layout.isDistributable(
getShapeOf(tdescTy)))
315 "TensorDesc shape is not distributable with the layout");
328 xegpu::CachePolicyAttr l1_hint,
329 xegpu::CachePolicyAttr l2_hint,
330 xegpu::CachePolicyAttr l3_hint,
331 xegpu::DistributeLayoutAttr layout) {
338 build(builder, state, retType, tensorDesc, dynamicOffsets, staticOffsetsAttr,
339 packed, transpose, l1_hint, l2_hint, l3_hint,
343LogicalResult LoadNdOp::verify() {
344 auto tdescTy = getTensorDescType();
347 if (tdescTy.getRank() > 2)
348 return emitOpError(
"Expects a 1D or 2D TensorDesc.\n");
351 return emitOpError(
"Invalid result, it should be a VectorType.\n");
354 return emitOpError(
"invalid l1_hint: ") << getL1HintAttr();
357 return emitOpError(
"invalid l2_hint: ") << getL2HintAttr();
360 return emitOpError(
"invalid l3_hint: ") << getL3HintAttr();
362 int tdescElems = tdescTy.getNumElements() * tdescTy.getArrayLength();
363 int valueElems = valueTy.getNumElements();
368 if (valueElems < tdescElems && valueTy.getRank() == 1) {
370 if (tdescTy.getLayoutAttr())
372 <<
"TensorDesc doesn't need LayoutAttr for SIMT code";
377 if (tdescElems % valueElems)
380 <<
" is not a valid distribution for tensor descriptor "
390 if (getTranspose()) {
391 auto trans = getTranspose().value();
393 if (llvm::all_of(trans, [&](
size_t s) {
return s < tdescShape.size(); }))
400 if (tdescTy.getRank() == 2) {
402 auto vnni_factor = valueShape.back();
403 tdescShape[axis] /= vnni_factor;
404 tdescShape.push_back(vnni_factor);
407 <<
"Invalid Packed Attr. It is ignored (available for 2D "
417 auto array_len = tdescTy.getArrayLength();
420 if (array_len > 1 && !tdescShape.empty()) {
421 stacked2DShape[0] *= array_len;
422 threeDShape.insert(threeDShape.begin(), array_len);
425 if (valueShape != stacked2DShape && valueShape != threeDShape)
427 <<
" is not consistent with tensor descriptor "
430 int64_t tDescRank = tdescTy.getRank();
431 int64_t offsetSize = getMixedOffsets().size();
432 if (offsetSize != tDescRank)
434 "Mismatched ranks between offsets and tensor descriptor");
436 if (
auto layout = getAnchorLayout()) {
437 if (!layout.isDistributable(
getShapeOf(tdescTy)))
439 "TensorDesc shape is not distributable with the layout");
451 xegpu::CachePolicyAttr l1_hint,
452 xegpu::CachePolicyAttr l2_hint,
453 xegpu::CachePolicyAttr l3_hint,
454 xegpu::DistributeLayoutAttr layout) {
461 build(builder, state, value, tensorDesc, dynamicOffsets, staticOffsetsAttr,
462 l1_hint, l2_hint, l3_hint, layout);
465LogicalResult StoreNdOp::verify() {
466 auto dstTy = getTensorDescType();
469 if (dstTy.getRank() > 2)
470 return emitOpError(
"Expects a 1D or 2D TensorDesc.\n");
473 return emitOpError(
"Expecting a VectorType result.\n");
476 return emitOpError(
"invalid l1_hint: ") << getL1HintAttr();
479 return emitOpError(
"invalid l2_hint: ") << getL2HintAttr();
482 return emitOpError(
"invalid l3_hint: ") << getL3HintAttr();
484 auto array_len = dstTy.getArrayLength();
486 return emitOpError(
"array length is not supported by store_nd.\n");
488 auto tdescElems = dstTy.getNumElements();
489 auto valueElems = valTy.getNumElements();
494 if (valTy.getRank() == 1 && valueElems < tdescElems) {
496 if (dstTy.getLayoutAttr())
498 <<
"TensorDesc doesn't need LayoutAttr for SIMT code";
500 if (tdescElems % valueElems)
503 <<
" is not a valid distribution for tensor descriptor " << dstTy;
511 if (tdescShape != valueShape)
513 <<
" is not consistent with tensor descriptor "
516 int64_t tDescRank = dstTy.getRank();
517 int64_t offsetSize = getMixedOffsets().size();
518 if (offsetSize != tDescRank)
520 "Mismatched ranks between offsets and tensor descriptor");
522 if (
auto layout = getAnchorLayout()) {
523 if (!layout.isDistributable(tdescShape))
525 "TensorDesc shape is not distributable with the layout");
534LogicalResult PrefetchOp::verify() {
536 return emitOpError(
"invalid l1_hint: ") << getL1HintAttr();
539 return emitOpError(
"invalid l2_hint: ") << getL2HintAttr();
542 return emitOpError(
"invalid l3_hint: ") << getL3HintAttr();
544 auto srcTy = getSourceType();
545 if (srcTy.
isInteger() && !getOffsetAlignByteAttr())
546 return emitOpError(
"offset_align_byte is required with integer source.");
548 if (getOffsetAlignByteAttr() && !srcTy.
isInteger())
549 return emitOpError(
"offset_align_byte only allowed with integer source.");
551 if (
auto layout = getAnchorLayout()) {
553 auto offsetsTy = getOffsets().getType();
554 if (llvm::isa<VectorType>(offsetsTy) &&
555 !layout.isDistributable(
getShapeOf(offsetsTy)))
556 return emitOpError(
"offset shape is not distributable with the layout");
565LogicalResult LoadGatherOp::verify() {
566 auto maskTy = getMaskType();
570 return emitOpError(
"invalid l1_hint: ") << getL1HintAttr();
573 return emitOpError(
"invalid l2_hint: ") << getL2HintAttr();
576 return emitOpError(
"invalid l3_hint: ") << getL3HintAttr();
578 auto srcTy = getSourceType();
579 uint64_t chunkSize =
static_cast<int64_t>(getChunkSize().value_or(1));
580 auto memTy = dyn_cast<MemRefType>(srcTy);
583 return emitError() <<
"Value should have the same element type as MemRef.";
585 if (
auto layout = getAnchorLayout()) {
586 if (!layout.isDistributable(
getShapeOf(valueTy)))
587 return emitOpError(
"Value shape is not distributable with the layout");
590 auto offsetsTy = getOffsets().getType();
598 IntegerAttr chunk_size, xegpu::CachePolicyAttr l1_hint,
599 xegpu::CachePolicyAttr l2_hint,
600 xegpu::CachePolicyAttr l3_hint) {
601 auto loc = source.
getLoc();
603 auto type = VectorType::get(size, builder.
getIndexType());
605 auto offset = vector::FromElementsOp::create(builder, loc, type, values);
607 build(builder, state, valueType, source, offset, mask, chunk_size, l1_hint,
608 l2_hint, l3_hint,
nullptr);
614 IntegerAttr chunk_size, xegpu::CachePolicyAttr l1_hint,
615 xegpu::CachePolicyAttr l2_hint,
616 xegpu::CachePolicyAttr l3_hint,
617 DistributeLayoutAttr layout) {
618 auto loc = source.
getLoc();
620 auto type = VectorType::get(size, builder.
getIndexType());
622 auto offset = vector::FromElementsOp::create(builder, loc, type, values);
624 build(builder, state, valueType, source, offset, mask, chunk_size, l1_hint,
625 l2_hint, l3_hint, layout);
631LogicalResult StoreScatterOp::verify() {
632 auto maskTy = getMaskType();
636 return emitOpError(
"invalid l1_hint: ") << getL1HintAttr();
639 return emitOpError(
"invalid l2_hint: ") << getL2HintAttr();
642 return emitOpError(
"invalid l3_hint: ") << getL3HintAttr();
644 auto destTy = getDestType();
645 uint64_t chunkSize =
static_cast<int64_t>(getChunkSize().value_or(1));
646 auto memTy = dyn_cast<MemRefType>(destTy);
649 return emitError() <<
"Value should have the same element type as MemRef.";
651 if (
auto layout = getAnchorLayout()) {
652 if (!layout.isDistributable(
getShapeOf(valueTy)))
653 return emitOpError(
"Value shape is not distributable with the layout");
656 auto offsetsTy = getOffsets().getType();
664 IntegerAttr chunk_size,
665 xegpu::CachePolicyAttr l1_hint,
666 xegpu::CachePolicyAttr l2_hint,
667 xegpu::CachePolicyAttr l3_hint) {
670 auto type = VectorType::get(size, builder.
getIndexType());
672 auto offset = vector::FromElementsOp::create(builder, loc, type, values);
675 build(builder, state, value, dest, offset, mask, chunk_size, l1_hint, l2_hint,
679void StoreScatterOp::build(
682 xegpu::CachePolicyAttr l1_hint, xegpu::CachePolicyAttr l2_hint,
683 xegpu::CachePolicyAttr l3_hint, DistributeLayoutAttr layout) {
686 auto type = VectorType::get(size, builder.
getIndexType());
688 auto offset = vector::FromElementsOp::create(builder, loc, type, values);
691 build(builder, state, value, dest, offset, mask, chunk_size, l1_hint, l2_hint,
702 std::optional<DistributeLayoutAttr> layout,
704 if (layout && !layout->isDistributable(
707 <<
" shape is not distributable with the layout";
717 auto aRank = aShape.size();
718 auto bRank = bShape.size();
719 auto resRank = resShape.size();
720 if (aRank == 1 && bRank == 1 && resRank == 1)
725 return op->
emitOpError(
"A operand must be a 2D vector.");
726 if (bRank < 2 || bRank > 3)
727 return op->
emitOpError(
"B operand must be a 2D or 3D vector.");
729 return op->
emitOpError(
"Result must be a 2D vector.");
732 int64_t bK = bRank == 3 ? bShape[0] * bShape[2] : bShape[0];
736 return op->
emitOpError(
"K-dimension mismatch: A has K=")
737 << aShape[1] <<
" but B has K=" << bK <<
".";
740 if (aShape[0] != resShape[0])
741 return op->
emitOpError(
"M-dimension mismatch: A has M=")
742 << aShape[0] <<
" but result has M=" << resShape[0] <<
".";
745 if (bShape[1] != resShape[1])
746 return op->
emitOpError(
"N-dimension mismatch: B has N=")
747 << bShape[1] <<
" but result has N=" << resShape[1] <<
".";
755 if (accType != resultType)
756 return op->
emitOpError(
"Accumulator type must match result type.");
763LogicalResult DpasOp::verify() {
764 auto lhsShape = getLhsType().getShape();
765 auto rhsShape = getRhsType().getShape();
766 auto resShape = getResultType().getShape();
788LogicalResult ConvertLayoutOp::verify() {
789 auto srcLayout = getInputLayout();
790 auto resLayout = getTargetLayout();
798 if ((!srcLayout.isForWorkgroup() || !resLayout.isForWorkgroup()) &&
799 (!srcLayout.isForSubgroup() || !resLayout.isForSubgroup()))
800 return emitOpError(
"expected input layout and target layout be WgLayout or "
801 "SgLayout at the same time.");
803 Type srcType = getSource().getType();
804 if (llvm::isa<VectorType>(srcType)) {
806 if (!srcLayout.isDistributable(
shape))
808 "invalid input layout, data cannot be evenly distributed.");
810 if (!resLayout.isDistributable(
shape))
812 "invalid target layout, data cannot be evenly distributed.");
814 return mlir::success();
823 DistributeLayoutAttr layout) {
830 build(builder, state, res, memDesc, dynamicOffsets, staticOffsetsAttr,
834LogicalResult LoadMatrixOp::verify() {
836 auto resTy = dyn_cast<VectorType>(getRes().
getType());
837 UnitAttr subgroup_block_io = getSubgroupBlockIoAttr();
838 MemDescType mdescTy = getMemDesc().getType();
841 getLayoutAttr(), [&]() {
return emitError(); });
850 DistributeLayoutAttr layout) {
855 build(builder, state, data, memDesc, dynamicOffsets, staticOffsetsAttr,
859LogicalResult StoreMatrixOp::verify() {
861 auto dataTy = dyn_cast<VectorType>(getData().
getType());
862 UnitAttr subgroup_block_io = getSubgroupBlockIoAttr();
863 MemDescType mdescTy = getMemDesc().getType();
865 getLayoutAttr(), [&]() {
return emitError(); });
872LogicalResult TruncfOp::verify() {
873 auto sourceVecType = dyn_cast<VectorType>(getSource().
getType());
874 auto resultVecType = dyn_cast<VectorType>(getResult().
getType());
876 if (sourceVecType.getElementTypeBitWidth() <=
877 resultVecType.getElementTypeBitWidth())
878 return emitOpError(
"input type must be wider than result type.");
887LogicalResult DpasMxOp::verify() {
888 auto aShape = getAType().getShape();
889 auto bShape = getBType().getShape();
890 auto resShape = getResultType().getShape();
912 auto scaleAVecType = dyn_cast<VectorType>(getScaleAType());
914 if (scaleAVecType && scaleAVecType.getRank() > 1) {
915 auto scaleAShape = scaleAVecType.getShape();
917 if (scaleAVecType.getRank() != 2)
918 return emitOpError(
"Scale A must be a 2D vector when not a scalar.");
922 scaleAShape,
"ScaleA")))
926 if (scaleAShape[0] != aShape[0])
928 << scaleAShape[0] <<
"] must match A M dimension [" << aShape[0]
935 auto scaleBVecType = dyn_cast<VectorType>(getScaleBType());
937 if (scaleBVecType && scaleBVecType.getRank() > 1) {
938 auto scaleBShape = scaleBVecType.getShape();
940 if (scaleBVecType.getRank() != 2)
941 return emitOpError(
"Scale B must be a 2D vector when not a scalar.");
945 scaleBShape,
"ScaleB")))
949 if (scaleBShape[1] != bShape[1])
951 << scaleBShape[1] <<
"] must match B N dimension [" << bShape[1]
958 if (getScaleA() && getScaleB()) {
959 auto scaleAVecType = dyn_cast<VectorType>(getScaleAType());
960 auto scaleBVecType = dyn_cast<VectorType>(getScaleBType());
962 if (scaleAVecType && scaleBVecType && scaleAVecType.getRank() > 1 &&
963 scaleBVecType.getRank() > 1) {
964 auto scaleAShape = scaleAVecType.getShape();
965 auto scaleBShape = scaleBVecType.getShape();
969 if (scaleAShape[1] != scaleBShape[0])
970 return emitOpError(
"Scale K dimension mismatch: scale_a has K=")
971 << scaleAShape[1] <<
" but scale_b has K=" << scaleBShape[0]
980#include <mlir/Dialect/XeGPU/IR/XeGPUAttrInterface.cpp.inc>
982#include <mlir/Dialect/XeGPU/IR/XeGPUEnums.cpp.inc>
983#define GET_OP_CLASSES
984#include <mlir/Dialect/XeGPU/IR/XeGPU.cpp.inc>
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 Type getValueType(Attribute attr)
static ArrayRef< int64_t > getShape(Type type)
Returns the shape of the given type.
static SmallVector< int64_t > getShapeOf(Type type)
static LogicalResult verifyDpasAccumulator(Operation *op, Type accType, Type resultType)
LogicalResult IsValidMatrixOpParams(VectorType dataTy, MemDescType mdescTy, UnitAttr subgroup_block_io, DistributeLayoutAttr layout, function_ref< InFlightDiagnostic()> emitError)
static std::string makeString(T array, bool breakline=false)
static bool isWriteHintOrNone(const CachePolicyAttr &attr)
static bool isReadHintOrNone(const CachePolicyAttr &attr)
static LogicalResult isValidGatherScatterBufferParams(Type offsetsTy, Type maskTy, VectorType valueTy, int64_t chunkSize, function_ref< InFlightDiagnostic()> emitError)
static LogicalResult verifyDpasDimensions(Operation *op, ArrayRef< int64_t > aShape, ArrayRef< int64_t > bShape, ArrayRef< int64_t > resShape)
static LogicalResult verifyLayoutDistributable(Operation *op, std::optional< DistributeLayoutAttr > layout, ArrayRef< int64_t > shape, StringRef operandName)
Attributes are known-constant values of operations.
DenseI64ArrayAttr getDenseI64ArrayAttr(ArrayRef< int64_t > values)
This class represents a diagnostic that is inflight and set to be reported.
This class helps build Operations.
Operation is the basic unit of execution within MLIR.
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...
bool isInteger() const
Return true if this is an integer type (with the specified width).
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Location getLoc() const
Return the location of this value.
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given memref value.
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.
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.
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.
llvm::function_ref< Fn > function_ref
This represents an operation in an abstracted form, suitable for use with the builder APIs.