MLIR 24.0.0git
AMDGPUToROCDL.cpp
Go to the documentation of this file.
1//===- AMDGPUToROCDL.cpp - AMDGPU to ROCDL dialect conversion -------===//
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
10
20#include "mlir/IR/Attributes.h"
23#include "mlir/IR/Matchers.h"
25#include "mlir/Pass/Pass.h"
26
28
29#include "llvm/ADT/STLExtras.h"
30#include "llvm/ADT/TypeSwitch.h"
31#include "llvm/Support/AMDGPUAddrSpace.h"
32#include "llvm/Support/Casting.h"
33#include "llvm/Support/ErrorHandling.h"
34#include "llvm/Support/MathExtras.h"
35#include <cstdint>
36#include <optional>
37
38namespace mlir {
39#define GEN_PASS_DEF_CONVERTAMDGPUTOROCDLPASS
40#include "mlir/Conversion/Passes.h.inc"
41} // namespace mlir
42
43using namespace mlir;
44using namespace mlir::amdgpu;
45
46/// Zero-extend or truncate the unsigned number `val` to `width` bits.
47static Value convertUnsignedToInt(ConversionPatternRewriter &rewriter,
48 Location loc, Value val, unsigned width) {
49 IntegerType destTy = rewriter.getIntegerType(width);
50 // Force check that `val` is of int type.
51 auto valTy = cast<IntegerType>(val.getType());
52 if (destTy == valTy)
53 return val;
54 return valTy.getWidth() > width
55 ? Value(LLVM::TruncOp::create(rewriter, loc, destTy, val))
56 : Value(LLVM::ZExtOp::create(rewriter, loc, destTy, val));
57}
58
59/// Convert an unsigned number `val` to i32.
60static Value convertUnsignedToI32(ConversionPatternRewriter &rewriter,
61 Location loc, Value val) {
62 return convertUnsignedToInt(rewriter, loc, val, 32);
63}
64
65static Value createI32Constant(ConversionPatternRewriter &rewriter,
66 Location loc, int32_t value) {
67 return LLVM::ConstantOp::create(rewriter, loc, rewriter.getI32Type(), value);
68}
69
70/// Convert an unsigned number `val` to i64.
71static Value convertUnsignedToI64(ConversionPatternRewriter &rewriter,
72 Location loc, Value val) {
73 return convertUnsignedToInt(rewriter, loc, val, 64);
74}
75
76static Value createI64Constant(ConversionPatternRewriter &rewriter,
77 Location loc, int64_t value) {
78 return LLVM::ConstantOp::create(rewriter, loc, rewriter.getI64Type(), value);
79}
80
81/// Returns the linear index used to access an element in the memref.
82static Value getLinearIndexI32(ConversionPatternRewriter &rewriter,
83 Location loc, MemRefDescriptor &memRefDescriptor,
85 IntegerType i32 = rewriter.getI32Type();
87 for (auto [i, increment, stride] : llvm::enumerate(indices, strides)) {
88 if (stride != 1) { // Skip if stride is 1.
89 Value strideValue =
90 ShapedType::isDynamic(stride)
91 ? convertUnsignedToI32(rewriter, loc,
92 memRefDescriptor.stride(rewriter, loc, i))
93 : LLVM::ConstantOp::create(rewriter, loc, i32, stride);
94 increment = LLVM::MulOp::create(rewriter, loc, increment, strideValue);
95 }
96 index = index ? LLVM::AddOp::create(rewriter, loc, index, increment)
97 : increment;
98 }
99 return index ? index : createI32Constant(rewriter, loc, 0);
100}
101
102/// Compute the contents of the `num_records` field for a given memref
103/// descriptor - that is, the number of bytes that's one element past the
104/// greatest possible valid index into the memref.
105static Value getNumRecords(ConversionPatternRewriter &rewriter, Location loc,
106 MemRefType memrefType,
107 MemRefDescriptor &memrefDescriptor,
108 ArrayRef<int64_t> strides, int64_t elementByteWidth,
109 unsigned numRecordsWidth, bool boundsCheck) {
110 if (numRecordsWidth > 32 && !boundsCheck)
111 return createI64Constant(rewriter, loc, llvm::maxUIntN(numRecordsWidth));
112 if (memrefType.hasStaticShape() &&
113 !llvm::any_of(strides, ShapedType::isDynamic)) {
114 int64_t size = memrefType.getRank() == 0 ? 1 : 0;
115 ArrayRef<int64_t> shape = memrefType.getShape();
116 for (uint32_t i = 0, e = memrefType.getRank(); i < e; ++i)
117 size = std::max(shape[i] * strides[i], size);
118 size = size * elementByteWidth;
119 return createI64Constant(rewriter, loc, size);
120 }
121 Value maxIndex;
122 for (uint32_t i = 0, e = memrefType.getRank(); i < e; ++i) {
123 Value size = memrefDescriptor.size(rewriter, loc, i);
124 Value stride = memrefDescriptor.stride(rewriter, loc, i);
125 Value maxThisDim = LLVM::MulOp::create(rewriter, loc, size, stride);
126 maxIndex = maxIndex
127 ? LLVM::UMaxOp::create(rewriter, loc, maxIndex, maxThisDim)
128 : maxThisDim;
129 }
130 Value maxIndexI64 = convertUnsignedToI64(rewriter, loc, maxIndex);
131 Value byteWidthConst = createI64Constant(rewriter, loc, elementByteWidth);
132 return LLVM::MulOp::create(rewriter, loc, maxIndexI64, byteWidthConst);
133}
134
135static Value makeBufferRsrc(ConversionPatternRewriter &rewriter, Location loc,
136 Value basePointer, Value numRecords,
137 bool boundsCheck, const ROCDL::TargetInfo &target,
138 unsigned numRecordsWidth,
139 Value cacheSwizzleStride = nullptr,
140 unsigned addressSpace = 8) {
141 // The stride value is generally 0. However, on MI-300 and onward, you can
142 // enable a cache swizzling mode by setting bit 14 of the stride field
143 // and setting that stride to a cache stride.
144 Type i16 = rewriter.getI16Type();
145 Value stride;
146 if (target.has(llvm::AMDGPU::FEAT_GFX940_INSTS) && cacheSwizzleStride) {
147 Value cacheStrideZext =
148 LLVM::ZExtOp::create(rewriter, loc, i16, cacheSwizzleStride);
149 Value swizzleBit = LLVM::ConstantOp::create(
150 rewriter, loc, i16, rewriter.getI16IntegerAttr(1 << 14));
151 stride = LLVM::OrOp::create(rewriter, loc, cacheStrideZext, swizzleBit,
152 /*isDisjoint=*/true);
153 } else {
154 stride = LLVM::ConstantOp::create(rewriter, loc, i16,
155 rewriter.getI16IntegerAttr(0));
156 }
157
158 uint32_t flags = 0;
159 if (target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS)) {
160 // Flag word:
161 // bit 0: swizzle
162 // bit 1: 0 means (total_offset + payload > numRecords)
163 // 1 means ((total_offset + payload >) numRecords) || ((offset +
164 // payload) > stride) only applied when swizzle_enable = 0. keep at
165 // zero.
166 // whether oob is done depends on numRecords.
167 // bits 2-3: Type (must be 0)
168 } else {
169 // Get the number of elements.
170 // Flag word:
171 // bits 0-11: dst sel, ignored by these intrinsics
172 // bits 12-14: data format (ignored, must be nonzero, 7=float)
173 // bits 15-18: data format (ignored, must be nonzero, 4=32bit)
174 // bit 19: In nested heap (0 here)
175 // bit 20: Behavior on unmap (0 means "return 0 / ignore")
176 // bits 21-22: Index stride for swizzles (N/A)
177 // bit 23: Add thread ID (0)
178 // bit 24: Reserved to 1 (RDNA) or 0 (CDNA)
179 // bits 25-26: Reserved (0)
180 // bit 27: Buffer is non-volatile (CDNA only)
181 // bits 28-29: Out of bounds select (0 = structured, 1 = check index, 2 =
182 // none, 3 = either swizzles or testing against offset field) RDNA only
183 // bits 30-31: Type (must be 0)
184 flags |= (7 << 12) | (4 << 15);
185 if (target.has(llvm::AMDGPU::FEAT_GFX10_INSTS)) {
186 flags |= (1 << 24);
187 uint32_t oob = boundsCheck ? 3 : 2;
188 flags |= (oob << 28);
189 }
190 }
191 Value flagsConst = createI32Constant(rewriter, loc, flags);
192 numRecords = convertUnsignedToInt(rewriter, loc, numRecords, numRecordsWidth);
193 Type rsrcType =
194 LLVM::LLVMPointerType::get(rewriter.getContext(), addressSpace);
195 Value resource = rewriter.createOrFold<ROCDL::MakeBufferRsrcOp>(
196 loc, rsrcType, basePointer, stride, numRecords, flagsConst);
197 return resource;
198}
199
200namespace {
201struct FatRawBufferCastLowering
202 : public ConvertOpToLLVMPattern<FatRawBufferCastOp> {
203 FatRawBufferCastLowering(const LLVMTypeConverter &converter,
204 const ROCDL::TargetInfo &target)
205 : ConvertOpToLLVMPattern<FatRawBufferCastOp>(converter), target(target) {}
206
207 ROCDL::TargetInfo target;
208
209 LogicalResult
210 matchAndRewrite(FatRawBufferCastOp op, FatRawBufferCastOpAdaptor adaptor,
211 ConversionPatternRewriter &rewriter) const override {
212 Location loc = op.getLoc();
213 Value memRef = adaptor.getSource();
214 Value unconvertedMemref = op.getSource();
215 MemRefType memrefType = cast<MemRefType>(unconvertedMemref.getType());
216 MemRefDescriptor descriptor(memRef);
217
218 DataLayout dataLayout = DataLayout::closest(op);
219 int64_t elementByteWidth =
220 dataLayout.getTypeSizeInBits(memrefType.getElementType()) / 8;
221
222 int64_t unusedOffset = 0;
223 SmallVector<int64_t, 5> strideVals;
224 if (failed(memrefType.getStridesAndOffset(strideVals, unusedOffset)))
225 return op.emitOpError("Can't lower non-stride-offset memrefs");
226
227 std::optional<unsigned> numRecordsWidth =
228 target.getBufferResourceNumRecordsWidth();
229 if (!numRecordsWidth)
230 return op.emitOpError(
231 "buffer resource num_records width is unknown for this target");
232
233 Value numRecords = adaptor.getValidBytes();
234 if (!numRecords)
235 numRecords = getNumRecords(rewriter, loc, memrefType, descriptor,
236 strideVals, elementByteWidth, *numRecordsWidth,
237 adaptor.getBoundsCheck());
238
239 Value basePointer =
240 adaptor.getResetOffset()
241 ? descriptor.bufferPtr(rewriter, loc, *getTypeConverter(),
242 memrefType)
243 : descriptor.alignedPtr(rewriter, loc);
244
245 Value offset =
246 adaptor.getResetOffset()
247 ? createIndexAttrConstant(rewriter, loc, getIndexType(), 0)
248 : descriptor.offset(rewriter, loc);
249
250 bool hasSizes = memrefType.getRank() > 0;
251 // No need to unpack() and pack() all the individual sizes and strides,
252 // so we'll just extract the arrays.
253 Value sizes = hasSizes
254 ? LLVM::ExtractValueOp::create(rewriter, loc, descriptor,
256 : Value{};
257 Value strides =
258 hasSizes ? LLVM::ExtractValueOp::create(rewriter, loc, descriptor,
260 : Value{};
261
262 Value fatPtr = makeBufferRsrc(
263 rewriter, loc, basePointer, numRecords, adaptor.getBoundsCheck(),
264 target, *numRecordsWidth, adaptor.getCacheSwizzleStride(),
265 /*addressSpace=*/7);
266
267 Value result = MemRefDescriptor::poison(
268 rewriter, loc,
269 getTypeConverter()->convertType(op.getResult().getType()));
270 SmallVector<int64_t> pos{kAllocatedPtrPosInMemRefDescriptor};
271 result = LLVM::InsertValueOp::create(rewriter, loc, result, fatPtr, pos);
272 result = LLVM::InsertValueOp::create(rewriter, loc, result, fatPtr,
274 result = LLVM::InsertValueOp::create(rewriter, loc, result, offset,
276 if (hasSizes) {
277 result = LLVM::InsertValueOp::create(rewriter, loc, result, sizes,
279 result = LLVM::InsertValueOp::create(rewriter, loc, result, strides,
281 }
282 rewriter.replaceOp(op, result);
283 return success();
284 }
285};
286
287/// Define lowering patterns for raw buffer ops
288template <typename GpuOp, typename Intrinsic>
289struct RawBufferOpLowering : public ConvertOpToLLVMPattern<GpuOp> {
290 RawBufferOpLowering(const LLVMTypeConverter &converter,
291 const ROCDL::TargetInfo &target)
292 : ConvertOpToLLVMPattern<GpuOp>(converter), target(target) {}
293
294 ROCDL::TargetInfo target;
295 static constexpr uint32_t maxVectorOpWidth = 128;
296
297 LogicalResult
298 matchAndRewrite(GpuOp gpuOp, typename GpuOp::Adaptor adaptor,
299 ConversionPatternRewriter &rewriter) const override {
300 Location loc = gpuOp.getLoc();
301 Value memref = adaptor.getMemref();
302 Value unconvertedMemref = gpuOp.getMemref();
303 MemRefType memrefType = cast<MemRefType>(unconvertedMemref.getType());
304
305 if (!target.has(llvm::AMDGPU::FEAT_GFX9_INSTS))
306 return gpuOp.emitOpError("raw buffer ops require GCN or higher");
307
308 Value storeData = adaptor.getODSOperands(0)[0];
309 if (storeData == memref) // no write component to this op
310 storeData = Value();
311 Type wantedDataType;
312 if (storeData)
313 wantedDataType = storeData.getType();
314 else
315 wantedDataType = gpuOp.getODSResults(0)[0].getType();
316
317 Value atomicCmpData = Value();
318 // Operand index 1 of a load is the indices, trying to read them can crash.
319 if (storeData) {
320 Value maybeCmpData = adaptor.getODSOperands(1)[0];
321 if (maybeCmpData != memref)
322 atomicCmpData = maybeCmpData;
323 }
324
325 Type llvmWantedDataType = this->typeConverter->convertType(wantedDataType);
326
327 Type i32 = rewriter.getI32Type();
328
329 // Get the type size in bytes.
330 DataLayout dataLayout = DataLayout::closest(gpuOp);
331 int64_t elementByteWidth =
332 dataLayout.getTypeSizeInBits(memrefType.getElementType()) / 8;
333 Value byteWidthConst = createI32Constant(rewriter, loc, elementByteWidth);
334
335 // If we want to load a vector<NxT> with total size <= 32
336 // bits, use a scalar load and bitcast it. Similarly, if bitsize(T) < 32
337 // and the total load size is >= 32, use a vector load of N / (bitsize(T) /
338 // 32) x i32 and bitcast. Also, the CAS intrinsic requires integer operands,
339 // so bitcast any floats to integers.
340 Type llvmBufferValType = llvmWantedDataType;
341 if (atomicCmpData) {
342 if (auto floatType = dyn_cast<FloatType>(wantedDataType))
343 llvmBufferValType = this->getTypeConverter()->convertType(
344 rewriter.getIntegerType(floatType.getWidth()));
345 }
346 if (auto dataVector = dyn_cast<VectorType>(wantedDataType)) {
347 uint32_t vecLen = dataVector.getNumElements();
348 uint32_t elemBits =
349 dataLayout.getTypeSizeInBits(dataVector.getElementType());
350 uint32_t totalBits = elemBits * vecLen;
351 bool usePackedFp16 =
352 isa_and_present<RawBufferAtomicFaddOp>(*gpuOp) && vecLen == 2;
353 if (totalBits > maxVectorOpWidth)
354 return gpuOp.emitOpError(
355 "Total width of loads or stores must be no more than " +
356 Twine(maxVectorOpWidth) + " bits, but we call for " +
357 Twine(totalBits) +
358 " bits. This should've been caught in validation");
359 if (!usePackedFp16 && elemBits < 32) {
360 if (totalBits > 32) {
361 if (totalBits % 32 != 0)
362 return gpuOp.emitOpError("Load or store of more than 32-bits that "
363 "doesn't fit into words. Can't happen\n");
364 llvmBufferValType = this->typeConverter->convertType(
365 VectorType::get(totalBits / 32, i32));
366 } else {
367 llvmBufferValType = this->typeConverter->convertType(
368 rewriter.getIntegerType(totalBits));
369 }
370 }
371 }
372 if (auto vecType = dyn_cast<VectorType>(llvmBufferValType)) {
373 // Buffer intrinsics doesn't support 1-element vectors, cast them to
374 // scalars.
375 if (vecType.getNumElements() == 1)
376 llvmBufferValType = vecType.getElementType();
377 }
378
379 SmallVector<Value, 6> args;
380 if (storeData) {
381 if (llvmBufferValType != llvmWantedDataType) {
382 Value castForStore = LLVM::BitcastOp::create(
383 rewriter, loc, llvmBufferValType, storeData);
384 args.push_back(castForStore);
385 } else {
386 args.push_back(storeData);
387 }
388 }
389
390 if (atomicCmpData) {
391 if (llvmBufferValType != llvmWantedDataType) {
392 Value castForCmp = LLVM::BitcastOp::create(
393 rewriter, loc, llvmBufferValType, atomicCmpData);
394 args.push_back(castForCmp);
395 } else {
396 args.push_back(atomicCmpData);
397 }
398 }
399
400 // Construct buffer descriptor from memref, attributes
401 int64_t offset = 0;
402 SmallVector<int64_t, 5> strides;
403 if (failed(memrefType.getStridesAndOffset(strides, offset)))
404 return gpuOp.emitOpError("Can't lower non-stride-offset memrefs");
405
406 MemRefDescriptor memrefDescriptor(memref);
407
408 Value ptr = memrefDescriptor.bufferPtr(
409 rewriter, loc, *this->getTypeConverter(), memrefType);
410 std::optional<unsigned> numRecordsWidth =
411 target.getBufferResourceNumRecordsWidth();
412 if (!numRecordsWidth)
413 return gpuOp.emitOpError(
414 "buffer resource num_records width is unknown for this target");
415
416 Value numRecords = getNumRecords(
417 rewriter, loc, memrefType, memrefDescriptor, strides, elementByteWidth,
418 *numRecordsWidth, adaptor.getBoundsCheck());
419 Value resource =
420 makeBufferRsrc(rewriter, loc, ptr, numRecords, adaptor.getBoundsCheck(),
421 target, *numRecordsWidth);
422 args.push_back(resource);
423
424 // Indexing (voffset)
425 Value voffset = getLinearIndexI32(rewriter, loc, memrefDescriptor,
426 adaptor.getIndices(), strides);
427 if (std::optional<int32_t> indexOffset = adaptor.getIndexOffset();
428 indexOffset && *indexOffset > 0) {
429 Value extraOffsetConst = createI32Constant(rewriter, loc, *indexOffset);
430 voffset = voffset ? LLVM::AddOp::create(rewriter, loc, voffset,
431 extraOffsetConst)
432 : extraOffsetConst;
433 }
434 voffset = LLVM::MulOp::create(rewriter, loc, voffset, byteWidthConst);
435 args.push_back(voffset);
436
437 // SGPR offset.
438 Value sgprOffset = adaptor.getSgprOffset();
439 if (!sgprOffset)
440 sgprOffset = createI32Constant(rewriter, loc, 0);
441 sgprOffset = LLVM::MulOp::create(rewriter, loc, sgprOffset, byteWidthConst);
442 args.push_back(sgprOffset);
443
444 llvm::SmallVector<Type, 1> resultTypes(gpuOp->getNumResults(),
445 llvmBufferValType);
446 typename Intrinsic::Properties properties{};
447 properties.aux = rewriter.getI32IntegerAttr(0);
448 Operation *lowered =
449 Intrinsic::create(rewriter, loc, resultTypes, args, properties);
450 if (lowered->getNumResults() == 1) {
451 Value replacement = lowered->getResult(0);
452 if (llvmBufferValType != llvmWantedDataType) {
453 replacement = LLVM::BitcastOp::create(rewriter, loc, llvmWantedDataType,
455 }
456 rewriter.replaceOp(gpuOp, replacement);
457 } else {
458 rewriter.eraseOp(gpuOp);
459 }
460 return success();
461 }
462};
463
464// TODO: AMDGPU backend already have all this bitpacking logic, we should move
465// it to some common place.
466/// Vmcnt, Expcnt and Lgkmcnt are decoded as follows:
467/// Vmcnt = Waitcnt[3:0] (pre-gfx9)
468/// Vmcnt = Waitcnt[15:14,3:0] (gfx9,10)
469/// Vmcnt = Waitcnt[15:10] (gfx11)
470/// Expcnt = Waitcnt[6:4] (pre-gfx11)
471/// Expcnt = Waitcnt[2:0] (gfx11)
472/// Lgkmcnt = Waitcnt[11:8] (pre-gfx10)
473/// Lgkmcnt = Waitcnt[13:8] (gfx10)
474/// Lgkmcnt = Waitcnt[9:4] (gfx11)
475static FailureOr<unsigned> encodeWaitcnt(const ROCDL::TargetInfo &target,
476 unsigned vmcnt, unsigned expcnt,
477 unsigned lgkmcnt) {
478 if (target.isUnknown())
479 return failure();
480 if (!target.has(llvm::AMDGPU::FEAT_GFX9_INSTS)) {
481 vmcnt = std::min(15u, vmcnt);
482 expcnt = std::min(7u, expcnt);
483 lgkmcnt = std::min(15u, lgkmcnt);
484 return vmcnt | (expcnt << 4) | (lgkmcnt << 8);
485 }
486 if (target.isGeneration(9)) {
487 vmcnt = std::min(63u, vmcnt);
488 expcnt = std::min(7u, expcnt);
489 lgkmcnt = std::min(15u, lgkmcnt);
490 unsigned lowBits = vmcnt & 0xF;
491 unsigned highBits = (vmcnt >> 4) << 14;
492 unsigned otherCnts = (expcnt << 4) | (lgkmcnt << 8);
493 return lowBits | highBits | otherCnts;
494 }
495 if (target.isGeneration(10)) {
496 vmcnt = std::min(63u, vmcnt);
497 expcnt = std::min(7u, expcnt);
498 lgkmcnt = std::min(63u, lgkmcnt);
499 unsigned lowBits = vmcnt & 0xF;
500 unsigned highBits = (vmcnt >> 4) << 14;
501 unsigned otherCnts = (expcnt << 4) | (lgkmcnt << 8);
502 return lowBits | highBits | otherCnts;
503 }
504 if (target.isGeneration(11)) {
505 vmcnt = std::min(63u, vmcnt);
506 expcnt = std::min(7u, expcnt);
507 lgkmcnt = std::min(63u, lgkmcnt);
508 return (vmcnt << 10) | expcnt | (lgkmcnt << 4);
509 }
510 return failure();
511}
512
513struct MemoryCounterWaitOpLowering
514 : public ConvertOpToLLVMPattern<MemoryCounterWaitOp> {
515 MemoryCounterWaitOpLowering(const LLVMTypeConverter &converter,
516 const ROCDL::TargetInfo &target)
517 : ConvertOpToLLVMPattern<MemoryCounterWaitOp>(converter), target(target) {
518 }
519
520 ROCDL::TargetInfo target;
521
522 LogicalResult
523 matchAndRewrite(MemoryCounterWaitOp op, OpAdaptor adaptor,
524 ConversionPatternRewriter &rewriter) const override {
525 if (target.has(llvm::AMDGPU::FEAT_GFX12_INSTS)) {
526 Location loc = op.getLoc();
527 if (std::optional<int> ds = adaptor.getDs())
528 ROCDL::WaitDscntOp::create(rewriter, loc, *ds);
529
530 if (std::optional<int> load = adaptor.getLoad())
531 ROCDL::WaitLoadcntOp::create(rewriter, loc, *load);
532
533 if (std::optional<int> store = adaptor.getStore())
534 ROCDL::WaitStorecntOp::create(rewriter, loc, *store);
535
536 if (std::optional<int> exp = adaptor.getExp())
537 ROCDL::WaitExpcntOp::create(rewriter, loc, *exp);
538
539 if (std::optional<int> tensor = adaptor.getTensor())
540 ROCDL::WaitTensorcntOp::create(rewriter, loc, *tensor);
541
542 rewriter.eraseOp(op);
543 return success();
544 }
545
546 if (adaptor.getTensor())
547 return op.emitOpError("unsupported chipset");
548
549 auto getVal = [](Attribute attr) -> unsigned {
550 if (attr)
551 return cast<IntegerAttr>(attr).getInt();
553 // This value will be clamped to the maximum value for the chipset.
554 return 1024;
555 };
556 unsigned ds = getVal(adaptor.getDsAttr());
557 unsigned exp = getVal(adaptor.getExpAttr());
559 unsigned vmcnt = 1024;
560 Attribute load = adaptor.getLoadAttr();
561 Attribute store = adaptor.getStoreAttr();
562 if (load && store) {
563 vmcnt = getVal(load) + getVal(store);
564 } else if (load) {
565 vmcnt = getVal(load);
566 } else if (store) {
567 vmcnt = getVal(store);
568 }
569
570 FailureOr<unsigned> waitcnt = encodeWaitcnt(target, vmcnt, exp, ds);
571 if (failed(waitcnt))
572 return op.emitOpError("unsupported chipset");
574 rewriter.replaceOpWithNewOp<ROCDL::SWaitcntOp>(op, *waitcnt);
575 return success();
577};
578
579struct LDSBarrierOpLowering : public ConvertOpToLLVMPattern<LDSBarrierOp> {
580 LDSBarrierOpLowering(const LLVMTypeConverter &converter,
582 : ConvertOpToLLVMPattern<LDSBarrierOp>(converter), target(target) {}
583
585
586 LogicalResult
587 matchAndRewrite(LDSBarrierOp op, LDSBarrierOp::Adaptor adaptor,
588 ConversionPatternRewriter &rewriter) const override {
589 Location loc = op.getLoc();
590 bool hasSplitBarriers = target.has(llvm::AMDGPU::FEAT_GFX12_INSTS);
591 // Inline assembly is only needed for cases where the hardware doesn't use
592 // split barriers and doesn't have FeatureBackOffBarrier (this is mainly
593 // early gfx9). In that case, we use inline assembly to bypass the
594 // conservative insertion of global memory waits at barriers, since we care
595 // more about performance than having debug watches work correctly.
596 bool requiresInlineAsm =
597 !hasSplitBarriers && !target.has(llvm::AMDGPU::FEAT_BACK_OFF_BARRIER);
598
599 Attribute mmra =
600 rewriter.getAttr<LLVM::MMRATagAttr>("amdgpu-synchronize-as", "local");
601 // Note: while there *is* a workgroup-one-as scope, this, when combined with
602 // the MMRA, will lead to the fence having no effect. This is because the
603 // codepaths for an atomic load or store will observe that a
604 // one-address-space atomic to LDS requires no synchronization because
605 // operations on LDS are totally ordered with respect to each other, and so
606 // will not emit the correct waitcnt operations that these fences are
607 // intended to produce. Therefore, we use a broader type of fence and rely
608 // on the MMRA to relax it to the semantics we want.
609 StringRef scope = "workgroup";
610
611 auto relFence = LLVM::FenceOp::create(rewriter, loc,
612 LLVM::AtomicOrdering::release, scope);
613 relFence->setDiscardableAttr(LLVM::LLVMDialect::getMmraAttrName(), mmra);
614 if (requiresInlineAsm) {
615 auto asmDialectAttr = LLVM::AsmDialectAttr::get(rewriter.getContext(),
616 LLVM::AsmDialect::AD_ATT);
617 const char *asmStr = ";;;WARNING: BREAKS DEBUG WATCHES\ns_barrier";
618 const char *constraints = "";
619 LLVM::InlineAsmOp::create(
620 rewriter, loc,
621 /*resultTypes=*/TypeRange(), /*operands=*/ValueRange(),
622 /*asm_string=*/asmStr, constraints, /*has_side_effects=*/true,
623 /*is_align_stack=*/false, LLVM::TailCallKind::None,
624 /*convergent=*/false,
625 /*asm_dialect=*/asmDialectAttr,
626 /*operand_attrs=*/ArrayAttr());
627 } else if (!hasSplitBarriers) {
628 ROCDL::SBarrierOp::create(rewriter, loc);
629 } else {
630 ROCDL::BarrierSignalOp::create(rewriter, loc, -1);
631 ROCDL::BarrierWaitOp::create(rewriter, loc, -1);
632 }
633
634 auto acqFence = LLVM::FenceOp::create(rewriter, loc,
635 LLVM::AtomicOrdering::acquire, scope);
636 acqFence->setDiscardableAttr(LLVM::LLVMDialect::getMmraAttrName(), mmra);
637 rewriter.replaceOp(op, acqFence);
638 return success();
639 }
640};
641
642struct SchedBarrierOpLowering : public ConvertOpToLLVMPattern<SchedBarrierOp> {
643 SchedBarrierOpLowering(const LLVMTypeConverter &converter,
644 const ROCDL::TargetInfo &target)
645 : ConvertOpToLLVMPattern<SchedBarrierOp>(converter), target(target) {}
646
647 ROCDL::TargetInfo target;
648
649 LogicalResult
650 matchAndRewrite(SchedBarrierOp op, SchedBarrierOp::Adaptor adaptor,
651 ConversionPatternRewriter &rewriter) const override {
652 rewriter.replaceOpWithNewOp<ROCDL::SchedBarrier>(op, op.getOptsAttr());
653 return success();
654 }
655};
656
657} // namespace
658
659/// Pack small float vector operands (fp4/fp6/fp8/bf16) into the format
660/// expected by scaled matrix multiply intrinsics (MFMA/WMMA).
661///
662/// Specifically:
663/// 1. If the element type is bfloat16, bitcast it to i16 unless rocdl intrinsic
664/// allows bf16. Newer MFMAs support bf16 types on operand, check
665/// IntrinsicsAMDGPU.td file for reference.
666/// 2. If instead we have a more than 64-bit quantity, use a <N / 4 x i32>
667/// instead, which is what the f8f6f4 intrinsics use.
668/// 3. If `input` is a vector of N <= 8 bytes, bitcast it to a (N * 8)-bit
669/// integer.
670///
671/// Note that the type of `input` has already been LLVM type converted:
672/// therefore 8-bit and smaller floats are represented as their corresponding
673/// `iN` integers.
674static Value packSmallFloatVectorOperand(ConversionPatternRewriter &rewriter,
675 Location loc, Value input,
676 bool allowBf16 = true) {
677 Type inputType = input.getType();
678 if (auto vectorType = dyn_cast<VectorType>(inputType)) {
679 if (vectorType.getElementType().isBF16() && !allowBf16)
680 return LLVM::BitcastOp::create(
681 rewriter, loc, vectorType.clone(rewriter.getI16Type()), input);
682 if (vectorType.getElementType().isInteger(8) &&
683 vectorType.getNumElements() <= 8)
684 return LLVM::BitcastOp::create(
685 rewriter, loc,
686 rewriter.getIntegerType(vectorType.getNumElements() * 8), input);
687 if (isa<IntegerType>(vectorType.getElementType()) &&
688 vectorType.getElementTypeBitWidth() <= 8) {
689 int64_t numWords = llvm::divideCeil(
690 vectorType.getNumElements() * vectorType.getElementTypeBitWidth(),
691 32);
692 return LLVM::BitcastOp::create(
693 rewriter, loc, VectorType::get(numWords, rewriter.getI32Type()),
694 input);
695 }
696 }
697 return input;
698}
699
700/// Converts packed vector operands to the expected ROCDL types.
701static Value convertPackedVectorOperand(ConversionPatternRewriter &rewriter,
702 Location loc, Value input,
703 bool allowBf16 = true) {
704 Type inputType = input.getType();
705 auto vectorType = cast<VectorType>(inputType);
706 // bf16 -> i16 when not allowed (pre-gfx950).
707 if (vectorType.getElementType().isBF16() && !allowBf16)
708 return LLVM::BitcastOp::create(
709 rewriter, loc, vectorType.clone(rewriter.getI16Type()), input);
710 // i8/fp8 vectors -> vector<Nxi32>.
711 if (isa<IntegerType>(vectorType.getElementType()) &&
712 vectorType.getElementTypeBitWidth() <= 8) {
713 int64_t numWords = llvm::divideCeil(
714 vectorType.getNumElements() * vectorType.getElementTypeBitWidth(), 32);
715 Type castType = (numWords > 1)
716 ? Type{VectorType::get(numWords, rewriter.getI32Type())}
717 : rewriter.getI32Type();
718 return LLVM::BitcastOp::create(rewriter, loc, castType, input);
719 }
720 return input;
721}
722
723/// Converts the scaled MFMA/WMMA operands, `scalesA` and `scalesB`, from MLIR
724/// AMDGPU dialect convention to ROCDL and LLVM AMDGPU intrinsics convention.
725///
726/// Specifically:
727/// 1. If `input` is a i8 value, zero extend it to i32
728/// 2. If `input` is a vector of length 4 or 8 and type i8, cast it to i32
729///
730/// Note that the type of `input` has already been LLVM type converted:
731/// therefore 8-bit and smaller floats are represented as their corresponding
732/// `iN` integers.
733static Value castScaleOperand(ConversionPatternRewriter &rewriter, Location loc,
734 Value input) {
735 return TypeSwitch<Type, Value>(input.getType())
736 .Case([&](IntegerType) {
737 // Handle scalar i8: zero extend to i32.
738 return LLVM::ZExtOp::create(rewriter, loc, rewriter.getI32Type(),
739 input);
740 })
741 .Case([&](VectorType vectorType) {
742 // Handle vector<4xi8> -> i32 or vector<8xi8> -> i64.
743 int64_t numElements = vectorType.getNumElements();
744 assert((numElements == 4 || numElements == 8) &&
745 "scale operand must be a vector of length 4 or 8");
746 IntegerType outputType =
747 (numElements == 4) ? rewriter.getI32Type() : rewriter.getI64Type();
748 return LLVM::BitcastOp::create(rewriter, loc, outputType, input);
749 })
750 .DefaultUnreachable("unexpected input type for scale operand");
751}
752
753/// Maps f8 scale element types to WMMA scale format codes.
754static std::optional<ROCDL::WMMAMatrixScaleFormat>
757 .Case([](Float8E8M0FNUType) { return ROCDL::WMMAMatrixScaleFormat::e8; })
758 .Case([](Float8E4M3FNType) { return ROCDL::WMMAMatrixScaleFormat::e4m3; })
759 .Default(std::nullopt);
760}
761
762/// Determines the ROCDL intrinsic name for scaled WMMA based on dimensions
763/// and scale block size (16 or 32).
764static std::optional<StringRef>
766 if (m == 16 && n == 16 && k == 128)
767 return isScale16
768 ? ROCDL::wmma_scale16_f32_16x16x128_f8f6f4::getOperationName()
769 : ROCDL::wmma_scale_f32_16x16x128_f8f6f4::getOperationName();
770
771 if (m == 32 && n == 16 && k == 128)
772 return isScale16 ? ROCDL::wmma_scale16_f32_32x16x128_f4::getOperationName()
773 : ROCDL::wmma_scale_f32_32x16x128_f4::getOperationName();
774
775 return std::nullopt;
776}
777
778/// Push an input operand. If it is a float type, nothing to do. If it is
779/// an integer type, then we need to also push its signdness (1 for signed, 0
780/// for unsigned) and we need to pack the input 16xi8 vector into a 4xi32
781/// vector (or the 8xi8 vector into a 2xi32 one for gfx12+).
782/// We also need to convert bfloat inputs to i16 to account for the bfloat
783/// intrinsics having been defined before the AMD backend supported bfloat. We
784/// similarly need to pack 8-bit float types into integers as if they were i8
785/// (which they are for the backend's purposes).
787 ConversionPatternRewriter &rewriter, Location loc,
788 const TypeConverter *typeConverter, bool isUnsigned, Value llvmInput,
789 Value mlirInput, SmallVectorImpl<Value> &operands,
790 SmallVectorImpl<NamedAttribute> &attrs, StringRef attrName) {
791 Type inputType = llvmInput.getType();
792 auto vectorType = dyn_cast<VectorType>(inputType);
793 if (!vectorType) {
794 operands.push_back(llvmInput);
795 return;
796 }
797 Type elemType = vectorType.getElementType();
798 if (elemType.getIntOrFloatBitWidth() > 8) {
799 operands.push_back(llvmInput);
800 return;
801 }
802
803 // We need to check the type of the input before conversion to properly test
804 // for int8. This is because, in LLVM, fp8 type is converted to int8, so the
805 // fp8/int8 information is lost during the conversion process.
806 auto mlirInputType = cast<VectorType>(mlirInput.getType());
807 bool isInputInteger = mlirInputType.getElementType().isInteger();
808 if (isInputInteger) {
809 // if element type is 8-bit signed or unsigned, ignore the isUnsigned flag
810 bool localIsUnsigned = isUnsigned;
811 if (elemType.isUnsignedInteger()) {
812 localIsUnsigned = true;
813 } else if (elemType.isSignedInteger()) {
814 localIsUnsigned = false;
815 }
816 attrs.push_back(
817 NamedAttribute(attrName, rewriter.getBoolAttr(!localIsUnsigned)));
818 }
819
820 int64_t numBits =
821 vectorType.getNumElements() * elemType.getIntOrFloatBitWidth();
822 Type i32 = rewriter.getI32Type();
823 Type intrinsicInType = numBits <= 32
824 ? (Type)rewriter.getIntegerType(numBits)
825 : (Type)VectorType::get(numBits / 32, i32);
826 auto llvmIntrinsicInType = typeConverter->convertType(intrinsicInType);
827 Value castInput = rewriter.createOrFold<LLVM::BitcastOp>(
828 loc, llvmIntrinsicInType, llvmInput);
829 // The wave64-mode 16x16x16 intrinsics that take 4-bit integers only need
830 // (256 / 64) * 4 = 16 bits of input (on gfx12+) but take i32 arguments.
831 // Add in the zeros here.
832 if (numBits < 32)
833 castInput = LLVM::ZExtOp::create(rewriter, loc, i32, castInput);
834 operands.push_back(castInput);
835}
836
837/// Push the output operand. For many cases this is only pushing the output in
838/// the operand list. But when we have f16 -> f16 or bf16 -> bf16 intrinsics,
839/// since the same numbers of VGPRs is used, we need to decide if to store the
840/// result in the upper 16 bits of the VGPRs or in the lower part. To store the
841/// result in the lower 16 bits, set subwordOffset to 1, otherwise result will
842/// be stored it in the upper part. The subwordOffset must not be set for gfx12,
843/// as the instructions have been changed to return fewer registers instead.
844static void wmmaPushOutputOperand(ConversionPatternRewriter &rewriter,
845 Location loc,
846 const TypeConverter *typeConverter,
847 Value output, int32_t subwordOffset,
848 bool clamp, SmallVectorImpl<Value> &operands,
850 Type inputType = output.getType();
851 auto vectorType = dyn_cast<VectorType>(inputType);
852 Type elemType = vectorType.getElementType();
853 operands.push_back(output);
854 if (elemType.isF16() || elemType.isBF16() || elemType.isInteger(16)) {
855 attrs.push_back(
856 NamedAttribute("opsel", rewriter.getBoolAttr(subwordOffset)));
857 } else if (elemType.isInteger(32)) {
858 attrs.push_back(NamedAttribute("clamp", rewriter.getBoolAttr(clamp)));
859 }
860}
861
862/// Return true if `type` is the E5M2 variant of an 8-bit float that is
863/// supported by the `_bf8` instructions on `target`.
865 Type type) {
866 return (target.hasFnuzFp8() && isa<Float8E5M2FNUZType>(type)) ||
867 (target.hasOcpFp8() && isa<Float8E5M2Type>(type));
868}
869
870/// Return true if `type` is the E4M3FN variant of an 8-bit float that is
871/// supported by the `_fp8` instructions on `target`.
873 Type type) {
874 return (target.hasFnuzFp8() && isa<Float8E4M3FNUZType>(type)) ||
875 (target.hasOcpFp8() && isa<Float8E4M3FNType>(type));
876}
877
878/// Return the `rocdl` intrinsic corresponding to a MFMA operation `mfma`
879/// if one exists. This includes checking to ensure the intrinsic is supported
880/// on the architecture you are compiling for.
881static std::optional<StringRef>
883 uint32_t m = mfma.getM(), n = mfma.getN(), k = mfma.getK(),
884 b = mfma.getBlocks();
885 Type sourceElem = getElementTypeOrSelf(mfma.getSourceA().getType());
886 Type destElem = getElementTypeOrSelf(mfma.getDestC().getType());
887
888 if (sourceElem.isF32() && destElem.isF32()) {
889 if (mfma.getReducePrecision() &&
890 target.has(llvm::AMDGPU::FEAT_XF32_INSTS)) {
891 if (m == 32 && n == 32 && k == 4 && b == 1)
892 return ROCDL::mfma_f32_32x32x4_xf32::getOperationName();
893 if (m == 16 && n == 16 && k == 8 && b == 1)
894 return ROCDL::mfma_f32_16x16x8_xf32::getOperationName();
895 }
896 if (m == 32 && n == 32 && k == 1 && b == 2)
897 return ROCDL::mfma_f32_32x32x1f32::getOperationName();
898 if (m == 16 && n == 16 && k == 1 && b == 4)
899 return ROCDL::mfma_f32_16x16x1f32::getOperationName();
900 if (m == 4 && n == 4 && k == 1 && b == 16)
901 return ROCDL::mfma_f32_4x4x1f32::getOperationName();
902 if (m == 32 && n == 32 && k == 2 && b == 1)
903 return ROCDL::mfma_f32_32x32x2f32::getOperationName();
904 if (m == 16 && n == 16 && k == 4 && b == 1)
905 return ROCDL::mfma_f32_16x16x4f32::getOperationName();
906 }
907
908 if (sourceElem.isF16() && destElem.isF32()) {
909 if (target.has(llvm::AMDGPU::FEAT_GFX950_INSTS)) {
910 if (m == 32 && n == 32 && k == 16 && b == 1)
911 return ROCDL::mfma_f32_32x32x16_f16::getOperationName();
912 if (m == 16 && n == 16 && k == 32 && b == 1)
913 return ROCDL::mfma_f32_16x16x32_f16::getOperationName();
914 }
915 if (m == 32 && n == 32 && k == 4 && b == 2)
916 return ROCDL::mfma_f32_32x32x4f16::getOperationName();
917 if (m == 16 && n == 16 && k == 4 && b == 4)
918 return ROCDL::mfma_f32_16x16x4f16::getOperationName();
919 if (m == 4 && n == 4 && k == 4 && b == 16)
920 return ROCDL::mfma_f32_4x4x4f16::getOperationName();
921 if (m == 32 && n == 32 && k == 8 && b == 1)
922 return ROCDL::mfma_f32_32x32x8f16::getOperationName();
923 if (m == 16 && n == 16 && k == 16 && b == 1)
924 return ROCDL::mfma_f32_16x16x16f16::getOperationName();
925 }
926
927 if (sourceElem.isBF16() && destElem.isF32()) {
928 if (target.has(llvm::AMDGPU::FEAT_GFX950_INSTS)) {
929 if (m == 32 && n == 32 && k == 16 && b == 1)
930 return ROCDL::mfma_f32_32x32x16_bf16::getOperationName();
931 if (m == 16 && n == 16 && k == 32 && b == 1)
932 return ROCDL::mfma_f32_16x16x32_bf16::getOperationName();
933 }
934 if (target.has(llvm::AMDGPU::FEAT_GFX90A_INSTS)) {
935 if (m == 32 && n == 32 && k == 4 && b == 2)
936 return ROCDL::mfma_f32_32x32x4bf16_1k::getOperationName();
937 if (m == 16 && n == 16 && k == 4 && b == 4)
938 return ROCDL::mfma_f32_16x16x4bf16_1k::getOperationName();
939 if (m == 4 && n == 4 && k == 4 && b == 16)
940 return ROCDL::mfma_f32_4x4x4bf16_1k::getOperationName();
941 if (m == 32 && n == 32 && k == 8 && b == 1)
942 return ROCDL::mfma_f32_32x32x8bf16_1k::getOperationName();
943 if (m == 16 && n == 16 && k == 16 && b == 1)
944 return ROCDL::mfma_f32_16x16x16bf16_1k::getOperationName();
945 }
946 if (m == 32 && n == 32 && k == 2 && b == 2)
947 return ROCDL::mfma_f32_32x32x2bf16::getOperationName();
948 if (m == 16 && n == 16 && k == 2 && b == 4)
949 return ROCDL::mfma_f32_16x16x2bf16::getOperationName();
950 if (m == 4 && n == 4 && k == 2 && b == 16)
951 return ROCDL::mfma_f32_4x4x2bf16::getOperationName();
952 if (m == 32 && n == 32 && k == 4 && b == 1)
953 return ROCDL::mfma_f32_32x32x4bf16::getOperationName();
954 if (m == 16 && n == 16 && k == 8 && b == 1)
955 return ROCDL::mfma_f32_16x16x8bf16::getOperationName();
956 }
957
958 if (sourceElem.isInteger(8) && destElem.isInteger(32)) {
959 if (target.has(llvm::AMDGPU::FEAT_GFX950_INSTS)) {
960 if (m == 32 && n == 32 && k == 32 && b == 1)
961 return ROCDL::mfma_i32_32x32x32_i8::getOperationName();
962 if (m == 16 && n == 16 && k == 64 && b == 1)
963 return ROCDL::mfma_i32_16x16x64_i8::getOperationName();
964 }
965 if (m == 32 && n == 32 && k == 4 && b == 2)
966 return ROCDL::mfma_i32_32x32x4i8::getOperationName();
967 if (m == 16 && n == 16 && k == 4 && b == 4)
968 return ROCDL::mfma_i32_16x16x4i8::getOperationName();
969 if (m == 4 && n == 4 && k == 4 && b == 16)
970 return ROCDL::mfma_i32_4x4x4i8::getOperationName();
971 if (m == 32 && n == 32 && k == 8 && b == 1)
972 return ROCDL::mfma_i32_32x32x8i8::getOperationName();
973 if (m == 16 && n == 16 && k == 16 && b == 1)
974 return ROCDL::mfma_i32_16x16x16i8::getOperationName();
975 if (m == 32 && n == 32 && k == 16 && b == 1 &&
976 target.has(llvm::AMDGPU::FEAT_GFX940_INSTS))
977 return ROCDL::mfma_i32_32x32x16_i8::getOperationName();
978 if (m == 16 && n == 16 && k == 32 && b == 1 &&
979 target.has(llvm::AMDGPU::FEAT_GFX940_INSTS))
980 return ROCDL::mfma_i32_16x16x32_i8::getOperationName();
981 }
982
983 if (sourceElem.isF64() && destElem.isF64() &&
984 target.has(llvm::AMDGPU::FEAT_GFX90A_INSTS)) {
985 if (m == 16 && n == 16 && k == 4 && b == 1)
986 return ROCDL::mfma_f64_16x16x4f64::getOperationName();
987 if (m == 4 && n == 4 && k == 4 && b == 4)
988 return ROCDL::mfma_f64_4x4x4f64::getOperationName();
989 }
990
991 if (destElem.isF32() && typeIsExpectedBf8ForTarget(target, sourceElem)) {
992 // Known to be correct because there are no scalar f8 instructions and
993 // because a length mismatch will have been caught by the verifier.
994 Type sourceBElem =
995 cast<VectorType>(mfma.getSourceB().getType()).getElementType();
996 if (m == 16 && n == 16 && k == 32 && b == 1) {
997 if (typeIsExpectedBf8ForTarget(target, sourceBElem))
998 return ROCDL::mfma_f32_16x16x32_bf8_bf8::getOperationName();
999 if (typeIsExpectedFp8ForTarget(target, sourceBElem))
1000 return ROCDL::mfma_f32_16x16x32_bf8_fp8::getOperationName();
1001 }
1002 if (m == 32 && n == 32 && k == 16 && b == 1) {
1003 if (typeIsExpectedBf8ForTarget(target, sourceBElem))
1004 return ROCDL::mfma_f32_32x32x16_bf8_bf8::getOperationName();
1005 if (typeIsExpectedFp8ForTarget(target, sourceBElem))
1006 return ROCDL::mfma_f32_32x32x16_bf8_fp8::getOperationName();
1007 }
1008 }
1009
1010 if (destElem.isF32() && typeIsExpectedFp8ForTarget(target, sourceElem)) {
1011 Type sourceBElem =
1012 cast<VectorType>(mfma.getSourceB().getType()).getElementType();
1013 if (m == 16 && n == 16 && k == 32 && b == 1) {
1014 if (typeIsExpectedBf8ForTarget(target, sourceBElem))
1015 return ROCDL::mfma_f32_16x16x32_fp8_bf8::getOperationName();
1016 if (typeIsExpectedFp8ForTarget(target, sourceBElem))
1017 return ROCDL::mfma_f32_16x16x32_fp8_fp8::getOperationName();
1018 }
1019 if (m == 32 && n == 32 && k == 16 && b == 1) {
1020 if (typeIsExpectedBf8ForTarget(target, sourceBElem))
1021 return ROCDL::mfma_f32_32x32x16_fp8_bf8::getOperationName();
1022 if (typeIsExpectedFp8ForTarget(target, sourceBElem))
1023 return ROCDL::mfma_f32_32x32x16_fp8_fp8::getOperationName();
1024 }
1025 }
1026
1027 return std::nullopt;
1028}
1029
1030static std::optional<ROCDL::MatrixFormat>
1033 mlirElemType)
1034 .Case([](Float8E4M3FNType) { return ROCDL::MatrixFormat::fp8_e4m3; })
1035 .Case([](Float8E5M2Type) { return ROCDL::MatrixFormat::fp8_e5m2; })
1036 .Case([](Float6E2M3FNType) { return ROCDL::MatrixFormat::fp6_e2m3; })
1037 .Case([](Float6E3M2FNType) { return ROCDL::MatrixFormat::fp6_e3m2; })
1038 .Case([](Float4E2M1FNType) { return ROCDL::MatrixFormat::fp4_e2m1; })
1039 .Default(std::nullopt);
1040}
1041
1042/// If there is a scaled MFMA instruction for the input element types `aType`
1043/// and `bType`, output type `destType`, problem size M, N, K, and B (number of
1044/// blocks) on the given `chipset`, return a tuple consisting of the
1045/// OperationName of the intrinsic and the type codes that need to be passed to
1046/// that intrinsic. Note that this is also used to implement some un-scaled
1047/// MFMAs, since the compiler represents the ordinary instruction as a "scaled"
1048/// MFMA with a scale of 0.
1050 std::tuple<StringRef, ROCDL::MatrixFormat, ROCDL::MatrixFormat>;
1051
1052static std::optional<ScaledMFMAIntrinsic>
1053mfmaOpToScaledIntrinsic(Type aType, Type bType, Type destType, uint32_t m,
1054 uint32_t n, uint32_t k, uint32_t b,
1055 const ROCDL::TargetInfo &target) {
1056 aType = getElementTypeOrSelf(aType);
1057 bType = getElementTypeOrSelf(bType);
1058 destType = getElementTypeOrSelf(destType);
1059
1060 if (!target.has(llvm::AMDGPU::FEAT_GFX950_INSTS))
1061 return std::nullopt;
1062 if (!isa<Float32Type>(destType))
1063 return std::nullopt;
1064
1065 std::optional<ROCDL::MatrixFormat> aTypeCode =
1067 std::optional<ROCDL::MatrixFormat> bTypeCode =
1069 if (!aTypeCode || !bTypeCode)
1070 return std::nullopt;
1071
1072 if (m == 32 && n == 32 && k == 64 && b == 1)
1073 return std::tuple{ROCDL::mfma_scale_f32_32x32x64_f8f6f4::getOperationName(),
1074 *aTypeCode, *bTypeCode};
1075 if (m == 16 && n == 16 && k == 128 && b == 1)
1076 return std::tuple{
1077 ROCDL::mfma_scale_f32_16x16x128_f8f6f4::getOperationName(), *aTypeCode,
1078 *bTypeCode};
1079
1080 return std::nullopt;
1081}
1082
1083static std::optional<ScaledMFMAIntrinsic>
1086 mfma.getSourceA().getType(), mfma.getSourceB().getType(),
1087 mfma.getDestC().getType(), mfma.getM(), mfma.getN(), mfma.getK(),
1088 mfma.getBlocks(), target);
1089}
1090
1091static std::optional<ScaledMFMAIntrinsic>
1093 return mfmaOpToScaledIntrinsic(smfma.getSourceA().getType(),
1094 smfma.getSourceB().getType(),
1095 smfma.getDestC().getType(), smfma.getM(),
1096 smfma.getN(), smfma.getK(), 1u, target);
1097}
1098
1099/// Returns the `rocdl` intrinsic corresponding to a WMMA operation `wmma`
1100/// for RDNA3/4 architectures.
1101static std::optional<StringRef>
1102wmmaOpToIntrinsicRDNA(Type elemSourceType, Type elemBSourceType,
1103 Type elemDestType, uint32_t k, bool isRDNA3) {
1104 using fp8 = Float8E4M3FNType;
1105 using bf8 = Float8E5M2Type;
1106
1107 // Handle k == 16 for RDNA3/4.
1108 if (k == 16) {
1109 // Common patterns for RDNA3 and RDNA4.
1110 if (elemSourceType.isF16() && elemDestType.isF32())
1111 return ROCDL::wmma_f32_16x16x16_f16::getOperationName();
1112 if (elemSourceType.isBF16() && elemDestType.isF32())
1113 return ROCDL::wmma_f32_16x16x16_bf16::getOperationName();
1114 if (elemSourceType.isF16() && elemDestType.isF16())
1115 return ROCDL::wmma_f16_16x16x16_f16::getOperationName();
1116 if (elemSourceType.isBF16() && elemDestType.isBF16())
1117 return ROCDL::wmma_bf16_16x16x16_bf16::getOperationName();
1118 if (elemSourceType.isInteger(8) && elemDestType.isInteger(32))
1119 return ROCDL::wmma_i32_16x16x16_iu8::getOperationName();
1120
1121 // RDNA3 specific patterns.
1122 if (isRDNA3) {
1123 if (elemSourceType.isInteger(4) && elemDestType.isInteger(32))
1124 return ROCDL::wmma_i32_16x16x16_iu4::getOperationName();
1125 return std::nullopt;
1126 }
1127
1128 // RDNA4 specific patterns (fp8/bf8).
1129 if (isa<fp8>(elemSourceType) && isa<fp8>(elemBSourceType) &&
1130 elemDestType.isF32())
1131 return ROCDL::wmma_f32_16x16x16_fp8_fp8::getOperationName();
1132 if (isa<fp8>(elemSourceType) && isa<bf8>(elemBSourceType) &&
1133 elemDestType.isF32())
1134 return ROCDL::wmma_f32_16x16x16_fp8_bf8::getOperationName();
1135 if (isa<bf8>(elemSourceType) && isa<bf8>(elemBSourceType) &&
1136 elemDestType.isF32())
1137 return ROCDL::wmma_f32_16x16x16_bf8_bf8::getOperationName();
1138 if (isa<bf8>(elemSourceType) && isa<fp8>(elemBSourceType) &&
1139 elemDestType.isF32())
1140 return ROCDL::wmma_f32_16x16x16_bf8_fp8::getOperationName();
1141 if (elemSourceType.isInteger(4) && elemDestType.isInteger(32))
1142 return ROCDL::wmma_i32_16x16x16_iu4::getOperationName();
1143
1144 return std::nullopt;
1145 }
1146
1147 // Handle k == 32 for RDNA4.
1148 if (k == 32 && !isRDNA3) {
1149 if (elemSourceType.isInteger(4) && elemDestType.isInteger(32))
1150 return ROCDL::wmma_i32_16x16x32_iu4::getOperationName();
1151 }
1152
1153 return std::nullopt;
1154}
1155
1156/// Return the `rocdl` intrinsic corresponding to a WMMA operation `wmma`
1157/// for the gfx1250 architecture.
1158static std::optional<StringRef> wmmaOpToIntrinsicGfx1250(Type elemSourceType,
1159 Type elemBSourceType,
1160 Type elemDestType,
1161 uint32_t k) {
1162 using fp8 = Float8E4M3FNType;
1163 using bf8 = Float8E5M2Type;
1164
1165 if (k == 4) {
1166 if (elemSourceType.isF32() && elemDestType.isF32())
1167 return ROCDL::wmma_f32_16x16x4_f32::getOperationName();
1168
1169 return std::nullopt;
1170 }
1171
1172 if (k == 32) {
1173 if (elemSourceType.isF16() && elemDestType.isF32())
1174 return ROCDL::wmma_f32_16x16x32_f16::getOperationName();
1175 if (elemSourceType.isBF16() && elemDestType.isF32())
1176 return ROCDL::wmma_f32_16x16x32_bf16::getOperationName();
1177 if (elemSourceType.isF16() && elemDestType.isF16())
1178 return ROCDL::wmma_f16_16x16x32_f16::getOperationName();
1179 if (elemSourceType.isBF16() && elemDestType.isBF16())
1180 return ROCDL::wmma_bf16_16x16x32_bf16::getOperationName();
1181
1182 return std::nullopt;
1183 }
1184
1185 if (k == 64) {
1186 if (isa<fp8>(elemSourceType) && isa<fp8>(elemBSourceType)) {
1187 if (elemDestType.isF32())
1188 return ROCDL::wmma_f32_16x16x64_fp8_fp8::getOperationName();
1189 if (elemDestType.isF16())
1190 return ROCDL::wmma_f16_16x16x64_fp8_fp8::getOperationName();
1191 }
1192 if (isa<fp8>(elemSourceType) && isa<bf8>(elemBSourceType)) {
1193 if (elemDestType.isF32())
1194 return ROCDL::wmma_f32_16x16x64_fp8_bf8::getOperationName();
1195 if (elemDestType.isF16())
1196 return ROCDL::wmma_f16_16x16x64_fp8_bf8::getOperationName();
1197 }
1198 if (isa<bf8>(elemSourceType) && isa<bf8>(elemBSourceType)) {
1199 if (elemDestType.isF32())
1200 return ROCDL::wmma_f32_16x16x64_bf8_bf8::getOperationName();
1201 if (elemDestType.isF16())
1202 return ROCDL::wmma_f16_16x16x64_bf8_bf8::getOperationName();
1203 }
1204 if (isa<bf8>(elemSourceType) && isa<fp8>(elemBSourceType)) {
1205 if (elemDestType.isF32())
1206 return ROCDL::wmma_f32_16x16x64_bf8_fp8::getOperationName();
1207 if (elemDestType.isF16())
1208 return ROCDL::wmma_f16_16x16x64_bf8_fp8::getOperationName();
1209 }
1210 if (elemSourceType.isInteger(8) && elemDestType.isInteger(32))
1211 return ROCDL::wmma_i32_16x16x64_iu8::getOperationName();
1212
1213 return std::nullopt;
1214 }
1215
1216 if (k == 128) {
1217 if (isa<fp8>(elemSourceType) && isa<fp8>(elemBSourceType)) {
1218 if (elemDestType.isF32())
1219 return ROCDL::wmma_f32_16x16x128_fp8_fp8::getOperationName();
1220 if (elemDestType.isF16())
1221 return ROCDL::wmma_f16_16x16x128_fp8_fp8::getOperationName();
1222 }
1223 if (isa<fp8>(elemSourceType) && isa<bf8>(elemBSourceType)) {
1224 if (elemDestType.isF32())
1225 return ROCDL::wmma_f32_16x16x128_fp8_bf8::getOperationName();
1226 if (elemDestType.isF16())
1227 return ROCDL::wmma_f16_16x16x128_fp8_bf8::getOperationName();
1228 }
1229 if (isa<bf8>(elemSourceType) && isa<bf8>(elemBSourceType)) {
1230 if (elemDestType.isF32())
1231 return ROCDL::wmma_f32_16x16x128_bf8_bf8::getOperationName();
1232 if (elemDestType.isF16())
1233 return ROCDL::wmma_f16_16x16x128_bf8_bf8::getOperationName();
1234 }
1235 if (isa<bf8>(elemSourceType) && isa<fp8>(elemBSourceType)) {
1236 if (elemDestType.isF32())
1237 return ROCDL::wmma_f32_16x16x128_bf8_fp8::getOperationName();
1238 if (elemDestType.isF16())
1239 return ROCDL::wmma_f16_16x16x128_bf8_fp8::getOperationName();
1240 }
1241
1242 return std::nullopt;
1243 }
1244
1245 return std::nullopt;
1246}
1247
1248/// Returns the `rocdl` intrinsic corresponding to a SparseMFMA (smfmac)
1249/// operation if one exists. This includes checking to ensure the intrinsic is
1250/// supported on the architecture you are compiling for.
1251static std::optional<StringRef>
1253 bool isGfx950 = target.has(llvm::AMDGPU::FEAT_GFX950_INSTS);
1254 auto isFp8 = [&](Type t) { return typeIsExpectedFp8ForTarget(target, t); };
1255 auto isBf8 = [&](Type t) { return typeIsExpectedBf8ForTarget(target, t); };
1256
1257 uint32_t m = op.getM(), n = op.getN(), k = op.getK();
1258 Type sourceAElem = getElementTypeOrSelf(op.getSourceA().getType());
1259 Type sourceBElem = getElementTypeOrSelf(op.getSourceB().getType());
1260 Type destElem = getElementTypeOrSelf(op.getDestC().getType());
1261
1262 if (m == 16 && n == 16 && k == 32) {
1263 if (sourceAElem.isF16() && sourceBElem.isF16() && destElem.isF32())
1264 return ROCDL::smfmac_f32_16x16x32_f16::getOperationName();
1265 if (sourceAElem.isBF16() && sourceBElem.isBF16() && destElem.isF32())
1266 return ROCDL::smfmac_f32_16x16x32_bf16::getOperationName();
1267 }
1268
1269 if (m == 16 && n == 16 && k == 64) {
1270 if (isGfx950) {
1271 if (sourceAElem.isF16() && sourceBElem.isF16() && destElem.isF32())
1272 return ROCDL::smfmac_f32_16x16x64_f16::getOperationName();
1273 if (sourceAElem.isBF16() && sourceBElem.isBF16() && destElem.isF32())
1274 return ROCDL::smfmac_f32_16x16x64_bf16::getOperationName();
1275 }
1276 if (sourceAElem.isInteger(8) && sourceBElem.isInteger(8) &&
1277 destElem.isInteger(32))
1278 return ROCDL::smfmac_i32_16x16x64_i8::getOperationName();
1279 if (isFp8(sourceAElem) && isFp8(sourceBElem) && destElem.isF32())
1280 return ROCDL::smfmac_f32_16x16x64_fp8_fp8::getOperationName();
1281 if (isFp8(sourceAElem) && isBf8(sourceBElem) && destElem.isF32())
1282 return ROCDL::smfmac_f32_16x16x64_fp8_bf8::getOperationName();
1283 if (isBf8(sourceAElem) && isFp8(sourceBElem) && destElem.isF32())
1284 return ROCDL::smfmac_f32_16x16x64_bf8_fp8::getOperationName();
1285 if (isBf8(sourceAElem) && isBf8(sourceBElem) && destElem.isF32())
1286 return ROCDL::smfmac_f32_16x16x64_bf8_bf8::getOperationName();
1287 }
1288
1289 if (m == 16 && n == 16 && k == 128 && isGfx950) {
1290 if (sourceAElem.isInteger(8) && sourceBElem.isInteger(8) &&
1291 destElem.isInteger(32))
1292 return ROCDL::smfmac_i32_16x16x128_i8::getOperationName();
1293 if (isFp8(sourceAElem) && isFp8(sourceBElem) && destElem.isF32())
1294 return ROCDL::smfmac_f32_16x16x128_fp8_fp8::getOperationName();
1295 if (isFp8(sourceAElem) && isBf8(sourceBElem) && destElem.isF32())
1296 return ROCDL::smfmac_f32_16x16x128_fp8_bf8::getOperationName();
1297 if (isBf8(sourceAElem) && isFp8(sourceBElem) && destElem.isF32())
1298 return ROCDL::smfmac_f32_16x16x128_bf8_fp8::getOperationName();
1299 if (isBf8(sourceAElem) && isBf8(sourceBElem) && destElem.isF32())
1300 return ROCDL::smfmac_f32_16x16x128_bf8_bf8::getOperationName();
1301 }
1302
1303 if (m == 32 && n == 32 && k == 16) {
1304 if (sourceAElem.isF16() && sourceBElem.isF16() && destElem.isF32())
1305 return ROCDL::smfmac_f32_32x32x16_f16::getOperationName();
1306 if (sourceAElem.isBF16() && sourceBElem.isBF16() && destElem.isF32())
1307 return ROCDL::smfmac_f32_32x32x16_bf16::getOperationName();
1308 }
1309
1310 if (m == 32 && n == 32 && k == 32) {
1311 if (isGfx950) {
1312 if (sourceAElem.isF16() && sourceBElem.isF16() && destElem.isF32())
1313 return ROCDL::smfmac_f32_32x32x32_f16::getOperationName();
1314 if (sourceAElem.isBF16() && sourceBElem.isBF16() && destElem.isF32())
1315 return ROCDL::smfmac_f32_32x32x32_bf16::getOperationName();
1316 }
1317 if (sourceAElem.isInteger(8) && sourceBElem.isInteger(8) &&
1318 destElem.isInteger(32))
1319 return ROCDL::smfmac_i32_32x32x32_i8::getOperationName();
1320 if (isFp8(sourceAElem) && isFp8(sourceBElem) && destElem.isF32())
1321 return ROCDL::smfmac_f32_32x32x32_fp8_fp8::getOperationName();
1322 if (isFp8(sourceAElem) && isBf8(sourceBElem) && destElem.isF32())
1323 return ROCDL::smfmac_f32_32x32x32_fp8_bf8::getOperationName();
1324 if (isBf8(sourceAElem) && isFp8(sourceBElem) && destElem.isF32())
1325 return ROCDL::smfmac_f32_32x32x32_bf8_fp8::getOperationName();
1326 if (isBf8(sourceAElem) && isBf8(sourceBElem) && destElem.isF32())
1327 return ROCDL::smfmac_f32_32x32x32_bf8_bf8::getOperationName();
1328 }
1329
1330 if (m == 32 && n == 32 && k == 64 && isGfx950) {
1331 if (sourceAElem.isInteger(8) && sourceBElem.isInteger(8) &&
1332 destElem.isInteger(32))
1333 return ROCDL::smfmac_i32_32x32x64_i8::getOperationName();
1334 if (isFp8(sourceAElem) && isFp8(sourceBElem) && destElem.isF32())
1335 return ROCDL::smfmac_f32_32x32x64_fp8_fp8::getOperationName();
1336 if (isFp8(sourceAElem) && isBf8(sourceBElem) && destElem.isF32())
1337 return ROCDL::smfmac_f32_32x32x64_fp8_bf8::getOperationName();
1338 if (isBf8(sourceAElem) && isFp8(sourceBElem) && destElem.isF32())
1339 return ROCDL::smfmac_f32_32x32x64_bf8_fp8::getOperationName();
1340 if (isBf8(sourceAElem) && isBf8(sourceBElem) && destElem.isF32())
1341 return ROCDL::smfmac_f32_32x32x64_bf8_bf8::getOperationName();
1342 }
1343
1344 return std::nullopt;
1345}
1346
1347/// Returns the `rocdl` intrinsic corresponding to a WMMA operation `wmma`
1348/// if one exists. This includes checking to ensure the intrinsic is supported
1349/// on the architecture you are compiling for.
1350static std::optional<StringRef>
1352 auto sourceVectorType = cast<VectorType>(wmma.getSourceA().getType());
1353 auto sourceBVectorType = cast<VectorType>(wmma.getSourceB().getType());
1354 auto destVectorType = cast<VectorType>(wmma.getDestC().getType());
1355 Type elemSourceType = sourceVectorType.getElementType();
1356 Type elemBSourceType = sourceBVectorType.getElementType();
1357 Type elemDestType = destVectorType.getElementType();
1358
1359 const uint32_t k = wmma.getK();
1360 const bool isRDNA3 = target.isGeneration(11);
1361 const bool isRDNA4 =
1362 target.isGeneration(12) && !target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS);
1363
1364 // Handle RDNA3 and RDNA4.
1365 if (isRDNA3 || isRDNA4)
1366 return wmmaOpToIntrinsicRDNA(elemSourceType, elemBSourceType, elemDestType,
1367 k, isRDNA3);
1368
1369 // Handle gfx1250.
1370 if (target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS))
1371 return wmmaOpToIntrinsicGfx1250(elemSourceType, elemBSourceType,
1372 elemDestType, k);
1373
1374 return std::nullopt;
1375}
1376
1377/// Returns the `rocdl` intrinsic corresponding to a SparseWMMA operation
1378/// `swmmac` if one exists. This includes checking to ensure the intrinsic is
1379/// supported on the architecture you are compiling for.
1381 StringRef name;
1385};
1386
1387static std::optional<SparseWMMAOpInfo>
1389 Type sourceAElem = getElementTypeOrSelf(swmmac.getSourceA().getType());
1390 Type sourceBElem = getElementTypeOrSelf(swmmac.getSourceB().getType());
1391 Type destElem = getElementTypeOrSelf(swmmac.getDestC().getType());
1392
1393 uint32_t m = swmmac.getM(), n = swmmac.getN(), k = swmmac.getK();
1394
1395 if ((m != 16) || (n != 16))
1396 return std::nullopt;
1397
1398 const bool isRDNA4 =
1399 target.isGeneration(12) && !target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS);
1400 if (isRDNA4) {
1401 if (k == 32) {
1402 if (destElem.isF32() && sourceAElem.isF16() && sourceBElem.isF16())
1403 return SparseWMMAOpInfo{
1404 ROCDL::swmmac_f32_16x16x32_f16::getOperationName(), false, false,
1405 false};
1406 if (destElem.isF32() && sourceAElem.isBF16() && sourceBElem.isBF16())
1407 return SparseWMMAOpInfo{
1408 ROCDL::swmmac_f32_16x16x32_bf16::getOperationName(), false, false,
1409 false};
1410 if (destElem.isF16() && sourceAElem.isF16() && sourceBElem.isF16())
1411 return SparseWMMAOpInfo{
1412 ROCDL::swmmac_f16_16x16x32_f16::getOperationName(), false, false,
1413 false};
1414 if (destElem.isBF16() && sourceAElem.isBF16() && sourceBElem.isBF16())
1415 return SparseWMMAOpInfo{
1416 ROCDL::swmmac_bf16_16x16x32_bf16::getOperationName(), false, false,
1417 false};
1418 if (destElem.isInteger(32) && sourceAElem.isInteger(8) &&
1419 sourceBElem.isInteger(8))
1420 return SparseWMMAOpInfo{
1421 ROCDL::swmmac_i32_16x16x32_iu8::getOperationName(), true, false,
1422 true};
1423 if (destElem.isInteger(32) && sourceAElem.isInteger(4) &&
1424 sourceBElem.isInteger(4))
1425 return SparseWMMAOpInfo{
1426 ROCDL::swmmac_i32_16x16x32_iu4::getOperationName(), true, false,
1427 true};
1428 if (destElem.isF32() && sourceAElem.isF8E4M3FN() &&
1429 sourceBElem.isF8E4M3FN())
1430 return SparseWMMAOpInfo{
1431 ROCDL::swmmac_f32_16x16x32_fp8_fp8::getOperationName(), false,
1432 false, false};
1433 if (destElem.isF32() && sourceAElem.isF8E4M3FN() &&
1434 sourceBElem.isF8E5M2())
1435 return SparseWMMAOpInfo{
1436 ROCDL::swmmac_f32_16x16x32_fp8_bf8::getOperationName(), false,
1437 false, false};
1438 if (destElem.isF32() && sourceAElem.isF8E5M2() &&
1439 sourceBElem.isF8E4M3FN())
1440 return SparseWMMAOpInfo{
1441 ROCDL::swmmac_f32_16x16x32_bf8_fp8::getOperationName(), false,
1442 false, false};
1443 if (destElem.isF32() && sourceAElem.isF8E5M2() && sourceBElem.isF8E5M2())
1444 return SparseWMMAOpInfo{
1445 ROCDL::swmmac_f32_16x16x32_bf8_bf8::getOperationName(), false,
1446 false, false};
1447 }
1448 if (k == 64) {
1449 if (destElem.isInteger(32) && sourceAElem.isInteger(4) &&
1450 sourceBElem.isInteger(4))
1451 return SparseWMMAOpInfo{
1452 ROCDL::swmmac_i32_16x16x64_iu4::getOperationName(), true, false,
1453 true};
1454 }
1455 }
1456
1457 const bool isGFX1250 = target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS);
1458 const bool isWavesize64 = swmmac.getWave64();
1459 if (isGFX1250 && !isWavesize64) {
1460 if (k == 64) {
1461 if (destElem.isF32() && sourceAElem.isF16() && sourceBElem.isF16())
1462 return SparseWMMAOpInfo{
1463 ROCDL::swmmac_f32_16x16x64_f16::getOperationName(), true, true,
1464 false};
1465 if (destElem.isF32() && sourceAElem.isBF16() && sourceBElem.isBF16())
1466 return SparseWMMAOpInfo{
1467 ROCDL::swmmac_f32_16x16x64_bf16::getOperationName(), true, true,
1468 false};
1469 if (destElem.isF16() && sourceAElem.isF16() && sourceBElem.isF16())
1470 return SparseWMMAOpInfo{
1471 ROCDL::swmmac_f16_16x16x64_f16::getOperationName(), true, true,
1472 false};
1473 if (destElem.isBF16() && sourceAElem.isBF16() && sourceBElem.isBF16())
1474 return SparseWMMAOpInfo{
1475 ROCDL::swmmac_bf16_16x16x64_bf16::getOperationName(), true, true,
1476 false};
1477 }
1478 if (k == 128) {
1479 if (destElem.isF32() && sourceAElem.isF8E4M3FN() &&
1480 sourceBElem.isF8E4M3FN())
1481 return SparseWMMAOpInfo{
1482 ROCDL::swmmac_f32_16x16x128_fp8_fp8::getOperationName(), false,
1483 true, false};
1484 if (destElem.isF32() && sourceAElem.isF8E4M3FN() &&
1485 sourceBElem.isF8E5M2())
1486 return SparseWMMAOpInfo{
1487 ROCDL::swmmac_f32_16x16x128_fp8_bf8::getOperationName(), false,
1488 true, false};
1489 if (destElem.isF32() && sourceAElem.isF8E5M2() &&
1490 sourceBElem.isF8E4M3FN())
1491 return SparseWMMAOpInfo{
1492 ROCDL::swmmac_f32_16x16x128_bf8_fp8::getOperationName(), false,
1493 true, false};
1494 if (destElem.isF32() && sourceAElem.isF8E5M2() && sourceBElem.isF8E5M2())
1495 return SparseWMMAOpInfo{
1496 ROCDL::swmmac_f32_16x16x128_bf8_bf8::getOperationName(), false,
1497 true, false};
1498 if (destElem.isF16() && sourceAElem.isF8E4M3FN() &&
1499 sourceBElem.isF8E4M3FN())
1500 return SparseWMMAOpInfo{
1501 ROCDL::swmmac_f16_16x16x128_fp8_fp8::getOperationName(), false,
1502 true, false};
1503 if (destElem.isF16() && sourceAElem.isF8E4M3FN() &&
1504 sourceBElem.isF8E5M2())
1505 return SparseWMMAOpInfo{
1506 ROCDL::swmmac_f16_16x16x128_fp8_bf8::getOperationName(), false,
1507 true, false};
1508 if (destElem.isF16() && sourceAElem.isF8E5M2() &&
1509 sourceBElem.isF8E4M3FN())
1510 return SparseWMMAOpInfo{
1511 ROCDL::swmmac_f16_16x16x128_bf8_fp8::getOperationName(), false,
1512 true, false};
1513 if (destElem.isF16() && sourceAElem.isF8E5M2() && sourceBElem.isF8E5M2())
1514 return SparseWMMAOpInfo{
1515 ROCDL::swmmac_f16_16x16x128_bf8_bf8::getOperationName(), false,
1516 true, false};
1517 if (destElem.isF16() && sourceAElem.isInteger(8) &&
1518 sourceBElem.isInteger(8))
1519 return SparseWMMAOpInfo{
1520 ROCDL::swmmac_f16_16x16x128_bf8_bf8::getOperationName(), false,
1521 true, false};
1522 if (destElem.isInteger(32) && sourceAElem.isInteger(8) &&
1523 sourceBElem.isInteger(8))
1524 return SparseWMMAOpInfo{
1525 ROCDL::swmmac_i32_16x16x128_iu8::getOperationName(), true, true,
1526 true};
1527 }
1528 }
1529
1530 return std::nullopt;
1531}
1532
1533namespace {
1534struct MFMAOpLowering : public ConvertOpToLLVMPattern<MFMAOp> {
1535 MFMAOpLowering(const LLVMTypeConverter &converter,
1536 const ROCDL::TargetInfo &target)
1537 : ConvertOpToLLVMPattern<MFMAOp>(converter), target(target) {}
1538
1539 ROCDL::TargetInfo target;
1540
1541 LogicalResult
1542 matchAndRewrite(MFMAOp op, MFMAOpAdaptor adaptor,
1543 ConversionPatternRewriter &rewriter) const override {
1544 Location loc = op.getLoc();
1545 Type destElem = getElementTypeOrSelf(op.getDestD().getType());
1546 Type outType = typeConverter->convertType(op.getDestD().getType());
1547 Type intrinsicOutType = outType;
1548 if (auto outVecType = dyn_cast<VectorType>(outType))
1549 if (outVecType.getElementType().isBF16())
1550 intrinsicOutType = outVecType.clone(rewriter.getI16Type());
1551
1552 if (!target.has(llvm::AMDGPU::FEAT_MAI_INSTS))
1553 return op->emitOpError("MFMA only supported on gfx908+");
1554 uint32_t getBlgpField = static_cast<uint32_t>(op.getBlgp());
1555 if (op.getNegateA() || op.getNegateB() || op.getNegateC()) {
1556 if (!target.has(llvm::AMDGPU::FEAT_GFX940_INSTS))
1557 return op.emitOpError("negation unsupported on older than gfx942");
1558 getBlgpField |=
1559 op.getNegateA() | (op.getNegateB() << 1) | (op.getNegateC() << 2);
1560 }
1561 std::optional<StringRef> maybeIntrinsic = mfmaOpToIntrinsic(op, target);
1562 std::optional<ScaledMFMAIntrinsic> maybeScaledIntrinsic =
1563 mfmaOpToScaledIntrinsic(op, target);
1564 if (!maybeIntrinsic.has_value() && !maybeScaledIntrinsic.has_value())
1565 return op.emitOpError("no intrinsic matching MFMA size on given chipset");
1566
1567 bool isScaled =
1568 !maybeIntrinsic.has_value() && maybeScaledIntrinsic.has_value();
1569 if (isScaled &&
1570 (adaptor.getAbid() > 0 || getBlgpField > 0 || op.getCbsz() > 0)) {
1571 return op.emitOpError(
1572 "non-default abid, blgp, and cbsz aren't supported on MFMAs that can "
1573 "be scaled as those fields are used for type information");
1574 }
1575
1576 StringRef intrinsicName =
1577 isScaled ? std::get<0>(*maybeScaledIntrinsic) : *maybeIntrinsic;
1578 // Determine if we can use bf16 in the intrinsic. Newer MFMAs in gfx950+
1579 // allows bf16 as the input. For reference check IntrinsicsAMDGPU.td file.
1580 bool allowBf16 = [&]() {
1581 if (!target.has(llvm::AMDGPU::FEAT_GFX950_INSTS))
1582 return false;
1583 if (isScaled)
1584 return true;
1585 return intrinsicName.contains("16x16x32.bf16") ||
1586 intrinsicName.contains("32x32x16.bf16");
1587 }();
1588 OperationState loweredOp(loc, intrinsicName);
1589 loweredOp.addTypes(intrinsicOutType);
1590 loweredOp.addOperands({packSmallFloatVectorOperand(
1591 rewriter, loc, adaptor.getSourceA(), allowBf16),
1593 rewriter, loc, adaptor.getSourceB(), allowBf16),
1594 adaptor.getDestC()});
1595 if (isScaled) {
1596 Value zero = createI32Constant(rewriter, loc, 0);
1597 auto [_scaledName, aTypeCode, bTypeCode] = *maybeScaledIntrinsic;
1598 loweredOp.addOperands({/*scale A=*/zero, /*scale B=*/zero});
1599 loweredOp.addAttributes(
1600 {{"cbsz",
1601 ROCDL::MatrixFormatAttr::get(rewriter.getContext(), aTypeCode)},
1602 {"blgp",
1603 ROCDL::MatrixFormatAttr::get(rewriter.getContext(), bTypeCode)},
1604 {"opselA", rewriter.getI32IntegerAttr(0)},
1605 {"opselB", rewriter.getI32IntegerAttr(0)}});
1606 } else {
1607 Attribute blgpAttr =
1608 destElem.isF64()
1609 ? Attribute(ROCDL::MFMANegModifierAttr::get(
1610 rewriter.getContext(),
1611 static_cast<ROCDL::MFMANegModifier>(getBlgpField)))
1612 : Attribute(ROCDL::MFMAPermBAttr::get(
1613 rewriter.getContext(),
1614 static_cast<ROCDL::MFMAPermB>(getBlgpField)));
1615 loweredOp.addAttributes(
1616 {{"cbsz", rewriter.getI32IntegerAttr(op.getCbsz())},
1617 {"abid", rewriter.getI32IntegerAttr(op.getAbid())},
1618 {"blgp", blgpAttr}});
1619 };
1620 Value lowered = rewriter.create(loweredOp)->getResult(0);
1621 if (outType != intrinsicOutType)
1622 lowered = LLVM::BitcastOp::create(rewriter, loc, outType, lowered);
1623 rewriter.replaceOp(op, lowered);
1624 return success();
1625 }
1626};
1627
1628struct ScaledMFMAOpLowering : public ConvertOpToLLVMPattern<ScaledMFMAOp> {
1629 ScaledMFMAOpLowering(const LLVMTypeConverter &converter,
1630 const ROCDL::TargetInfo &target)
1631 : ConvertOpToLLVMPattern(converter), target(target) {}
1632
1633 ROCDL::TargetInfo target;
1634
1635 LogicalResult
1636 matchAndRewrite(ScaledMFMAOp op, ScaledMFMAOpAdaptor adaptor,
1637 ConversionPatternRewriter &rewriter) const override {
1638 Location loc = op.getLoc();
1639 Type intrinsicOutType = typeConverter->convertType(op.getDestD().getType());
1640
1641 if (!target.has(llvm::AMDGPU::FEAT_GFX950_INSTS))
1642 return op->emitOpError("scaled MFMA only supported on gfx908+");
1643 std::optional<ScaledMFMAIntrinsic> maybeScaledIntrinsic =
1644 mfmaOpToScaledIntrinsic(op, target);
1645 if (!maybeScaledIntrinsic.has_value())
1646 return op.emitOpError(
1647 "no intrinsic matching scaled MFMA size on given chipset");
1648
1649 auto [intrinsicName, aTypeCode, bTypeCode] = *maybeScaledIntrinsic;
1650 OperationState loweredOp(loc, intrinsicName);
1651 loweredOp.addTypes(intrinsicOutType);
1652 loweredOp.addOperands(
1653 {packSmallFloatVectorOperand(rewriter, loc, adaptor.getSourceA()),
1654 packSmallFloatVectorOperand(rewriter, loc, adaptor.getSourceB()),
1655 adaptor.getDestC()});
1656 loweredOp.addOperands(
1657 {/*scales A*/
1658 castScaleOperand(rewriter, loc, adaptor.getScalesA()),
1659 /*scales B*/
1660 castScaleOperand(rewriter, loc, adaptor.getScalesB())});
1661 loweredOp.addAttributes(
1662 {{"cbsz",
1663 ROCDL::MatrixFormatAttr::get(rewriter.getContext(), aTypeCode)},
1664 {"blgp",
1665 ROCDL::MatrixFormatAttr::get(rewriter.getContext(), bTypeCode)},
1666 {"opselA", rewriter.getI32IntegerAttr(adaptor.getScalesIdxA())},
1667 {"opselB", rewriter.getI32IntegerAttr(adaptor.getScalesIdxB())}});
1668
1669 Value lowered = rewriter.create(loweredOp)->getResult(0);
1670 rewriter.replaceOp(op, lowered);
1671 return success();
1672 }
1673};
1674
1675struct SparseMFMAOpLowering : public ConvertOpToLLVMPattern<SparseMFMAOp> {
1676 SparseMFMAOpLowering(const LLVMTypeConverter &converter,
1677 const ROCDL::TargetInfo &target)
1678 : ConvertOpToLLVMPattern<SparseMFMAOp>(converter), target(target) {}
1679
1680 ROCDL::TargetInfo target;
1681
1682 LogicalResult
1683 matchAndRewrite(SparseMFMAOp op, SparseMFMAOpAdaptor adaptor,
1684 ConversionPatternRewriter &rewriter) const override {
1685 Location loc = op.getLoc();
1686 auto outType =
1687 typeConverter->convertType<VectorType>(op.getDestC().getType());
1688 if (!outType)
1689 return rewriter.notifyMatchFailure(op, "type conversion failed");
1690
1691 // smfmac is supported on gfx942 and gfx950.
1692 if (!target.has(llvm::AMDGPU::FEAT_GFX940_INSTS))
1693 return op->emitOpError("sparse MFMA (smfmac) only supported on gfx942+");
1694
1695 std::optional<StringRef> maybeIntrinsic = smfmacOpToIntrinsic(op, target);
1696 if (!maybeIntrinsic.has_value())
1697 return op.emitOpError(
1698 "no intrinsic matching sparse MFMA on the given chipset");
1699 bool isGfx942BF16 =
1700 (*maybeIntrinsic ==
1701 ROCDL::smfmac_f32_16x16x32_bf16::getOperationName() ||
1702 *maybeIntrinsic ==
1703 ROCDL::smfmac_f32_32x32x16_bf16::getOperationName());
1704 bool isGfx950 =
1705 (target.has(llvm::AMDGPU::FEAT_GFX950_INSTS)) && !isGfx942BF16;
1706
1707 Value a = convertPackedVectorOperand(rewriter, loc, adaptor.getSourceA(),
1708 isGfx950);
1709 Value b = convertPackedVectorOperand(rewriter, loc, adaptor.getSourceB(),
1710 isGfx950);
1711 Value c = adaptor.getDestC();
1712
1713 // Bitcast sparse indices from vector<4xi8> or vector<2xi16> to i32.
1714 // gfx950 8-bit variants already carry the index as i32; skip the bitcast.
1715 Value sparseIdx = adaptor.getSparseIdx();
1716 Type i32Type = rewriter.getI32Type();
1717 if (sparseIdx.getType() != i32Type)
1718 sparseIdx = LLVM::BitcastOp::create(rewriter, loc, i32Type, sparseIdx);
1719
1720 OperationState loweredOp(loc, maybeIntrinsic.value());
1721 loweredOp.addTypes(outType);
1722 loweredOp.addOperands({a, b, c, sparseIdx});
1723 loweredOp.addAttributes(
1724 {{"cbsz", rewriter.getI32IntegerAttr(op.getCbsz())},
1725 {"abid", rewriter.getI32IntegerAttr(op.getAbid())}});
1726 Value lowered = rewriter.create(loweredOp)->getResult(0);
1727 rewriter.replaceOp(op, lowered);
1728 return success();
1729 }
1730};
1731
1732struct WMMAOpLowering : public ConvertOpToLLVMPattern<WMMAOp> {
1733 WMMAOpLowering(const LLVMTypeConverter &converter,
1734 const ROCDL::TargetInfo &target)
1735 : ConvertOpToLLVMPattern<WMMAOp>(converter), target(target) {}
1736
1737 ROCDL::TargetInfo target;
1738
1739 LogicalResult
1740 matchAndRewrite(WMMAOp op, WMMAOpAdaptor adaptor,
1741 ConversionPatternRewriter &rewriter) const override {
1742 Location loc = op.getLoc();
1743 auto outType =
1744 typeConverter->convertType<VectorType>(op.getDestD().getType());
1745 if (!outType)
1746 return rewriter.notifyMatchFailure(op, "type conversion failed");
1747
1748 if (!target.isGeneration(11) && !target.isGeneration(12))
1749 return op->emitOpError("WMMA only supported on gfx11 and gfx12");
1750
1751 bool isGFX1250 = target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS);
1752
1753 // The WMMA operations represent vectors of bf16s as vectors of i16s
1754 // (except on gfx1250), so we need to bitcast bfloats to i16 and then
1755 // bitcast them back.
1756 auto aType = cast<VectorType>(adaptor.getSourceA().getType());
1757 auto bType = cast<VectorType>(adaptor.getSourceB().getType());
1758 auto destCType = cast<VectorType>(adaptor.getDestC().getType());
1759 bool castAToI16 = aType.getElementType().isBF16() && !isGFX1250;
1760 bool castBToI16 = bType.getElementType().isBF16() && !isGFX1250;
1761 bool castDestCToI16 = destCType.getElementType().isBF16() && !isGFX1250;
1762 bool castOutToI16 = outType.getElementType().isBF16() && !isGFX1250;
1763 VectorType rawOutType = outType;
1764 if (castOutToI16)
1765 rawOutType = outType.clone(rewriter.getI16Type());
1766 Value a = adaptor.getSourceA();
1767 if (castAToI16)
1768 a = LLVM::BitcastOp::create(rewriter, loc,
1769 aType.clone(rewriter.getI16Type()), a);
1770 Value b = adaptor.getSourceB();
1771 if (castBToI16)
1772 b = LLVM::BitcastOp::create(rewriter, loc,
1773 bType.clone(rewriter.getI16Type()), b);
1774 Value destC = adaptor.getDestC();
1775 if (castDestCToI16)
1776 destC = LLVM::BitcastOp::create(
1777 rewriter, loc, destCType.clone(rewriter.getI16Type()), destC);
1778
1779 std::optional<StringRef> maybeIntrinsic = wmmaOpToIntrinsic(op, target);
1780
1781 if (!maybeIntrinsic.has_value())
1782 return op.emitOpError("no intrinsic matching WMMA on the given chipset");
1783
1784 if (target.has(llvm::AMDGPU::FEAT_GFX12_INSTS) &&
1785 op.getSubwordOffset() != 0)
1786 return op.emitOpError("subwordOffset not supported on gfx12+");
1787
1788 SmallVector<Value, 4> operands;
1789 SmallVector<NamedAttribute, 4> attrs;
1790 wmmaPushInputOperand(rewriter, loc, typeConverter, op.getUnsignedA(), a,
1791 op.getSourceA(), operands, attrs, "signA");
1792 wmmaPushInputOperand(rewriter, loc, typeConverter, op.getUnsignedB(), b,
1793 op.getSourceB(), operands, attrs, "signB");
1794 wmmaPushOutputOperand(rewriter, loc, typeConverter, destC,
1795 op.getSubwordOffset(), op.getClamp(), operands,
1796 attrs);
1797
1798 OperationState loweredOp(loc, *maybeIntrinsic);
1799 loweredOp.addTypes(rawOutType);
1800 loweredOp.addOperands(operands);
1801 loweredOp.addAttributes(attrs);
1802 Operation *lowered = rewriter.create(loweredOp);
1803
1804 Operation *maybeCastBack = lowered;
1805 if (rawOutType != outType)
1806 maybeCastBack = LLVM::BitcastOp::create(rewriter, loc, outType,
1807 lowered->getResult(0));
1808 rewriter.replaceOp(op, maybeCastBack->getResults());
1809
1810 return success();
1811 }
1812};
1813
1814enum class DotFamily {
1815 /// ROCDL_Dot_IntrOp: single `clamp` attribute.
1816 Clamp,
1817 /// ROCDL_Dot_NoClamp_IntrOp: no attributes.
1818 NoClamp,
1819 /// ROCDL_Sudot_IntrOp: `signA`, `signB`, and `clamp` attributes.
1820 Sudot,
1821};
1822
1823static std::optional<std::pair<StringRef, DotFamily>>
1824dotOpToIntrinsic(DotOp op, const ROCDL::TargetInfo &target) {
1825 Type aElem = cast<VectorType>(op.getSourceA().getType()).getElementType();
1826 Type bElem = cast<VectorType>(op.getSourceB().getType()).getElementType();
1827 Type dest = op.getDestC().getType();
1828 bool uA = op.getUnsignedA();
1829 bool uB = op.getUnsignedB();
1830
1831 // f16 x f16 -> f32 / f16.
1832 if (aElem.isF16() && bElem.isF16()) {
1833 if (dest.isF32() && target.has(llvm::AMDGPU::FEAT_DOT10_INSTS))
1834 return {{ROCDL::fdot2::getOperationName(), DotFamily::Clamp}};
1835 if (dest.isF16() && target.has(llvm::AMDGPU::FEAT_DOT9_INSTS))
1836 return {{ROCDL::fdot2_f16_f16::getOperationName(), DotFamily::NoClamp}};
1837 return std::nullopt;
1838 }
1839
1840 // bf16 x bf16 -> f32 / bf16.
1841 if (aElem.isBF16() && bElem.isBF16()) {
1842 if (dest.isF32() && target.has(llvm::AMDGPU::FEAT_DOT12_INSTS))
1843 return {{ROCDL::fdot2_f32_bf16::getOperationName(), DotFamily::Clamp}};
1844 if (dest.isBF16() && target.has(llvm::AMDGPU::FEAT_DOT9_INSTS))
1845 return {{ROCDL::fdot2_bf16_bf16::getOperationName(), DotFamily::NoClamp}};
1846 return std::nullopt;
1847 }
1848
1849 // Integer sources -> i32.
1850 if (isa<IntegerType>(aElem) && isa<IntegerType>(bElem) &&
1851 dest.isInteger(32)) {
1852 bool mixedSign = (uA != uB);
1853 unsigned elemWidth = aElem.getIntOrFloatBitWidth();
1854
1855 if (mixedSign) {
1856 if (!target.has(llvm::AMDGPU::FEAT_DOT8_INSTS))
1857 return std::nullopt;
1858 StringRef name;
1859 switch (elemWidth) {
1860 case 8:
1861 name = ROCDL::sudot4::getOperationName();
1862 break;
1863 case 4:
1864 name = ROCDL::sudot8::getOperationName();
1865 break;
1866 default:
1867 return std::nullopt;
1868 }
1869 return {{name, DotFamily::Sudot}};
1870 }
1871
1872 StringRef name;
1873 bool supported = false;
1874 switch (elemWidth) {
1875 case 16:
1876 supported = target.has(llvm::AMDGPU::FEAT_DOT2_INSTS);
1877 name = uA ? ROCDL::udot2::getOperationName()
1878 : ROCDL::sdot2::getOperationName();
1879 break;
1880 case 8:
1881 supported = uA ? target.has(llvm::AMDGPU::FEAT_DOT7_INSTS)
1882 : target.has(llvm::AMDGPU::FEAT_DOT1_INSTS) ||
1883 target.has(llvm::AMDGPU::FEAT_DOT8_INSTS);
1884 name = uA ? ROCDL::udot4::getOperationName()
1885 : ROCDL::sdot4::getOperationName();
1886 break;
1887 case 4:
1888 supported = uA ? target.has(llvm::AMDGPU::FEAT_DOT7_INSTS)
1889 : target.has(llvm::AMDGPU::FEAT_DOT1_INSTS) ||
1890 target.has(llvm::AMDGPU::FEAT_DOT8_INSTS);
1891 name = uA ? ROCDL::udot8::getOperationName()
1892 : ROCDL::sdot8::getOperationName();
1893 break;
1894 default:
1895 return std::nullopt;
1896 }
1897 if (!supported)
1898 return std::nullopt;
1899 return {{name, DotFamily::Clamp}};
1900 }
1901
1902 // fp8/bf8 x fp8/bf8 -> f32.
1903 bool aIsFp8 = isa<Float8E4M3FNType>(aElem);
1904 bool aIsBf8 = isa<Float8E5M2Type>(aElem);
1905 bool bIsFp8 = isa<Float8E4M3FNType>(bElem);
1906 bool bIsBf8 = isa<Float8E5M2Type>(bElem);
1907 if ((aIsFp8 || aIsBf8) && (bIsFp8 || bIsBf8) && dest.isF32()) {
1908 if (!target.has(llvm::AMDGPU::FEAT_DOT11_INSTS))
1909 return std::nullopt;
1910 StringRef name;
1911 if (aIsFp8 && bIsFp8)
1912 name = ROCDL::dot4_f32_fp8_fp8::getOperationName();
1913 else if (aIsFp8 && bIsBf8)
1914 name = ROCDL::dot4_f32_fp8_bf8::getOperationName();
1915 else if (aIsBf8 && bIsFp8)
1916 name = ROCDL::dot4_f32_bf8_fp8::getOperationName();
1917 else
1918 name = ROCDL::dot4_f32_bf8_bf8::getOperationName();
1919 return {{name, DotFamily::NoClamp}};
1920 }
1921
1922 return std::nullopt;
1923}
1924
1925struct DotOpLowering : public ConvertOpToLLVMPattern<DotOp> {
1926 DotOpLowering(const LLVMTypeConverter &converter,
1927 const ROCDL::TargetInfo &target)
1928 : ConvertOpToLLVMPattern<DotOp>(converter), target(target) {}
1929
1930 ROCDL::TargetInfo target;
1931
1932 LogicalResult
1933 matchAndRewrite(DotOp op, DotOpAdaptor adaptor,
1934 ConversionPatternRewriter &rewriter) const override {
1935 Location loc = op.getLoc();
1936
1937 std::optional<std::pair<StringRef, DotFamily>> maybeIntrinsic =
1938 dotOpToIntrinsic(op, target);
1939 if (!maybeIntrinsic)
1940 return op.emitOpError("no intrinsic matching dot on the given chipset: ")
1941 << op.getSourceA().getType() << " * " << op.getSourceB().getType()
1942 << " + " << op.getDestC().getType();
1943
1944 auto [intrinsicName, family] = maybeIntrinsic.value();
1945
1946 Value a = convertPackedVectorOperand(rewriter, loc, adaptor.getSourceA());
1947 Value b = convertPackedVectorOperand(rewriter, loc, adaptor.getSourceB());
1948 Value c = adaptor.getDestC();
1949
1950 SmallVector<NamedAttribute, 3> attrs;
1951 if (family == DotFamily::Sudot) {
1952 attrs.push_back(rewriter.getNamedAttr(
1953 "signA", rewriter.getBoolAttr(!op.getUnsignedA())));
1954 attrs.push_back(rewriter.getNamedAttr(
1955 "signB", rewriter.getBoolAttr(!op.getUnsignedB())));
1956 }
1957
1958 if (family != DotFamily::NoClamp && op.getClamp())
1959 attrs.push_back(
1960 rewriter.getNamedAttr("clamp", rewriter.getBoolAttr(true)));
1961
1962 Type resultType = typeConverter->convertType(op.getDestD().getType());
1963
1964 OperationState loweredOp(loc, intrinsicName);
1965 loweredOp.addTypes(resultType);
1966 loweredOp.addOperands({a, b, c});
1967 loweredOp.addAttributes(attrs);
1968 Operation *lowered = rewriter.create(loweredOp);
1969 rewriter.replaceOp(op, lowered->getResults());
1970 return success();
1971 }
1972};
1973
1974struct SparseWMMAOpLowering : public ConvertOpToLLVMPattern<SparseWMMAOp> {
1975 SparseWMMAOpLowering(const LLVMTypeConverter &converter,
1976 const ROCDL::TargetInfo &target)
1977 : ConvertOpToLLVMPattern<SparseWMMAOp>(converter), target(target) {}
1978
1979 ROCDL::TargetInfo target;
1980
1981 LogicalResult
1982 matchAndRewrite(SparseWMMAOp op, SparseWMMAOpAdaptor adaptor,
1983 ConversionPatternRewriter &rewriter) const override {
1984 Location loc = op.getLoc();
1985 auto outType =
1986 typeConverter->convertType<VectorType>(op.getDestD().getType());
1987 if (!outType)
1988 return rewriter.notifyMatchFailure(op, "type conversion failed");
1989
1990 std::optional<SparseWMMAOpInfo> maybeIntrinsic =
1991 sparseWMMAOpToIntrinsic(op, target);
1992
1993 if (!maybeIntrinsic.has_value())
1994 return op.emitOpError(
1995 "no intrinsic matching Sparse WMMA on the given chipset");
1996 SparseWMMAOpInfo intrinsic = maybeIntrinsic.value();
1997
1998 SmallVector<NamedAttribute> attrs;
1999
2000 if ((op.getUnsignedA() || op.getUnsignedB()) && !intrinsic.useSign)
2001 return op->emitOpError("intrinsic doesn't support unsign");
2002 if (intrinsic.useSign) {
2003 if (auto attr = op.getUnsignedAAttr())
2004 attrs.push_back({"signA", attr});
2005 if (auto attr = op.getUnsignedBAttr())
2006 attrs.push_back({"signB", attr});
2007 }
2008
2009 if ((op.getReuseA() || op.getReuseB()) && !intrinsic.useReuse)
2010 return op->emitOpError("intrinsic doesn't support reuse");
2011 if (intrinsic.useReuse) {
2012 if (auto attr = op.getReuseAAttr())
2013 attrs.push_back({"reuseA", attr});
2014 if (auto attr = op.getReuseBAttr())
2015 attrs.push_back({"reuseB", attr});
2016 }
2017
2018 if (op.getClamp() && !intrinsic.useClamp)
2019 return op->emitOpError("intrinsic doesn't support clamp");
2020 if (intrinsic.useClamp && op.getClampAttr())
2021 attrs.push_back({"clamp", op.getClampAttr()});
2022
2023 const bool isGFX1250orHigher = target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS);
2024 Value a = convertPackedVectorOperand(rewriter, loc, adaptor.getSourceA(),
2025 isGFX1250orHigher);
2026 Value b = convertPackedVectorOperand(rewriter, loc, adaptor.getSourceB(),
2027 isGFX1250orHigher);
2028 Value c = adaptor.getDestC();
2029 VectorType rawOutType = outType;
2030 if (!isGFX1250orHigher) {
2031 c = convertPackedVectorOperand(rewriter, loc, adaptor.getDestC(), false);
2032 rawOutType = cast<VectorType>(c.getType());
2033 }
2034
2035 // Bitcast sparse indices from vector<4xi8> to i32.
2036 Value sparseIdx = LLVM::BitcastOp::create(
2037 rewriter, loc, rewriter.getI32Type(), adaptor.getSparseIdx());
2038
2039 OperationState loweredOp(loc, intrinsic.name);
2040 loweredOp.addTypes(rawOutType);
2041 loweredOp.addOperands({a, b, c, sparseIdx});
2042 loweredOp.addAttributes(attrs);
2043 Operation *lowered = rewriter.create(loweredOp);
2044
2045 Operation *maybeCastBack = lowered;
2046 if (rawOutType != outType)
2047 maybeCastBack = LLVM::BitcastOp::create(rewriter, loc, outType,
2048 lowered->getResult(0));
2049 rewriter.replaceOp(op, maybeCastBack->getResults());
2050
2051 return success();
2052 }
2053};
2054
2055struct ScaledWMMAOpLowering : public ConvertOpToLLVMPattern<ScaledWMMAOp> {
2056 ScaledWMMAOpLowering(const LLVMTypeConverter &converter,
2057 const ROCDL::TargetInfo &target)
2058 : ConvertOpToLLVMPattern<ScaledWMMAOp>(converter), target(target) {}
2059
2060 ROCDL::TargetInfo target;
2061
2062 LogicalResult
2063 matchAndRewrite(ScaledWMMAOp op, ScaledWMMAOpAdaptor adaptor,
2064 ConversionPatternRewriter &rewriter) const override {
2065 Location loc = op.getLoc();
2066 auto outType =
2067 typeConverter->convertType<VectorType>(op.getDestD().getType());
2068 if (!outType)
2069 return rewriter.notifyMatchFailure(op, "type conversion failed");
2070
2071 if (!target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS))
2072 return op->emitOpError("WMMA scale only supported on gfx1250+");
2073
2074 int64_t m = op.getM();
2075 int64_t n = op.getN();
2076 int64_t k = op.getK();
2077
2078 Type aElemType = getElementTypeOrSelf(op.getSourceA().getType());
2079 Type bElemType = getElementTypeOrSelf(op.getSourceB().getType());
2080
2081 std::optional<ROCDL::MatrixFormat> aFmtCode =
2083 std::optional<ROCDL::MatrixFormat> bFmtCode =
2085
2086 if (!aFmtCode || !bFmtCode)
2087 return op.emitOpError("unsupported element types for scaled_wmma");
2088
2089 // Get scale vector types and determine variant (scale vs scale16).
2090 auto scaleAVecType = cast<VectorType>(op.getScaleA().getType());
2091 auto scaleBVecType = cast<VectorType>(op.getScaleB().getType());
2092
2093 if (scaleAVecType.getNumElements() != scaleBVecType.getNumElements())
2094 return op.emitOpError("scaleA and scaleB must have equal vector length");
2095
2096 // Extract scale format from element types.
2097 Type scaleAElemType = scaleAVecType.getElementType();
2098 Type scaleBElemType = scaleBVecType.getElementType();
2099
2100 std::optional<ROCDL::WMMAMatrixScaleFormat> scaleAFmt =
2101 getWmmaScaleFormat(scaleAElemType);
2102 std::optional<ROCDL::WMMAMatrixScaleFormat> scaleBFmt =
2103 getWmmaScaleFormat(scaleBElemType);
2104
2105 if (!scaleAFmt || !scaleBFmt)
2106 return op.emitOpError("unsupported scale element types");
2107
2108 // Determine which intrinsic to use based on dimensions.
2109 bool isScale16 = (scaleAVecType.getNumElements() == 8);
2110 std::optional<StringRef> intrinsicName =
2111 getScaledWmmaIntrinsicName(m, n, k, isScale16);
2112 if (!intrinsicName)
2113 return op.emitOpError("unsupported scaled_wmma dimensions: ")
2114 << m << "x" << n << "x" << k;
2115
2116 SmallVector<NamedAttribute, 8> attrs;
2117
2118 // The f4 variant does not have fmtA and fmtB attributes.
2119 bool is32x16 = (m == 32 && n == 16 && k == 128);
2120 if (!is32x16) {
2121 attrs.emplace_back("fmtA", ROCDL::MatrixFormatAttr::get(
2122 rewriter.getContext(), *aFmtCode));
2123 attrs.emplace_back("fmtB", ROCDL::MatrixFormatAttr::get(
2124 rewriter.getContext(), *bFmtCode));
2125 }
2126
2127 // modC uses default value of 0.
2128 attrs.emplace_back(
2129 "modC", ROCDL::WMMACModifierAttr::get(rewriter.getContext(),
2130 ROCDL::WMMACModifier::none));
2131
2132 // Scale attributes. Convert user-facing firstScaleLane (0 or 16) to the
2133 // half of the wave that is being selected (0 or 1).
2134 attrs.emplace_back("scaleAType", ROCDL::WMMAMatrixScaleAttr::get(
2135 rewriter.getContext(),
2136 static_cast<ROCDL::WMMAMatrixScale>(
2137 op.getAFirstScaleLane() / 16)));
2138 attrs.emplace_back("fmtScaleA", ROCDL::WMMAMatrixScaleFormatAttr::get(
2139 rewriter.getContext(), *scaleAFmt));
2140 attrs.emplace_back("scaleBType", ROCDL::WMMAMatrixScaleAttr::get(
2141 rewriter.getContext(),
2142 static_cast<ROCDL::WMMAMatrixScale>(
2143 op.getBFirstScaleLane() / 16)));
2144 attrs.emplace_back("fmtScaleB", ROCDL::WMMAMatrixScaleFormatAttr::get(
2145 rewriter.getContext(), *scaleBFmt));
2146
2147 // Reuse flags use default value of false.
2148 attrs.emplace_back("reuseA", rewriter.getBoolAttr(false));
2149 attrs.emplace_back("reuseB", rewriter.getBoolAttr(false));
2150
2151 // Convert typed float vectors to packed format.
2152 Value sourceA =
2153 packSmallFloatVectorOperand(rewriter, loc, adaptor.getSourceA());
2154 Value sourceB =
2155 packSmallFloatVectorOperand(rewriter, loc, adaptor.getSourceB());
2156
2157 // Pack scale vectors into i32/i64.
2158 Value packedScaleA = castScaleOperand(rewriter, loc, adaptor.getScaleA());
2159 Value packedScaleB = castScaleOperand(rewriter, loc, adaptor.getScaleB());
2160
2161 // Create the intrinsic call.
2162 OperationState loweredOp(loc, *intrinsicName);
2163 loweredOp.addTypes(outType);
2164 loweredOp.addOperands(
2165 {sourceA, sourceB, adaptor.getDestC(), packedScaleA, packedScaleB});
2166 loweredOp.addAttributes(attrs);
2167
2168 Operation *lowered = rewriter.create(loweredOp);
2169 rewriter.replaceOp(op, lowered->getResults());
2170
2171 return success();
2172 }
2173};
2174
2175struct TransposeLoadOpLowering
2176 : public ConvertOpToLLVMPattern<TransposeLoadOp> {
2177 TransposeLoadOpLowering(const LLVMTypeConverter &converter,
2178 const ROCDL::TargetInfo &target)
2179 : ConvertOpToLLVMPattern<TransposeLoadOp>(converter), target(target) {}
2180
2181 ROCDL::TargetInfo target;
2182
2183 LogicalResult
2184 matchAndRewrite(TransposeLoadOp op, TransposeLoadOpAdaptor adaptor,
2185 ConversionPatternRewriter &rewriter) const override {
2186 if (!target.has(llvm::AMDGPU::FEAT_GFX950_INSTS) &&
2187 !target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS))
2188 return op.emitOpError(
2189 "transpose_load is only supported on gfx950 and gfx1250+");
2190
2191 Location loc = op.getLoc();
2192 auto srcMemRefType = cast<MemRefType>(op.getSrc().getType());
2193
2194 // Elements in subbyte memrefs are stored non-contiguously,
2195 // reject if source is sub-byte memref. Use emulated memrefs instead.
2196 size_t srcElementSize =
2197 srcMemRefType.getElementType().getIntOrFloatBitWidth();
2198 if (srcElementSize < 8)
2199 return op.emitOpError("Expect source memref to have at least 8 bits "
2200 "element size, got ")
2201 << srcElementSize;
2202
2203 auto resultType = cast<VectorType>(op.getResult().getType());
2204 Value srcPtr =
2205 getStridedElementPtr(rewriter, loc, srcMemRefType, adaptor.getSrc(),
2206 (adaptor.getSrcIndices()));
2207
2208 size_t numElements = resultType.getNumElements();
2209 size_t elementTypeSize =
2210 resultType.getElementType().getIntOrFloatBitWidth();
2211
2212 Type llvmResultType = typeConverter->convertType(resultType);
2213 // ROCDL transpose load intrinsics return vectors of 32-bit integers for
2214 // sub-16-bit element types, and otherwise return the converted result type.
2215 Type rocdlResultType =
2216 elementTypeSize < 16
2217 ? VectorType::get((numElements * elementTypeSize) / 32,
2218 rewriter.getIntegerType(32))
2219 : llvmResultType;
2220
2221 auto emitNumElementsError = [&](size_t expected, StringRef chipsetName) {
2222 return op.emitOpError()
2223 << elementTypeSize << "-bit transpose_load requires " << expected
2224 << " elements on " << chipsetName;
2225 };
2226
2227 Value intrinsic;
2228 if (target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS)) {
2229 switch (elementTypeSize) {
2230 case 4: {
2231 if (numElements != 16)
2232 return emitNumElementsError(16, "gfx1250+");
2233 intrinsic =
2234 ROCDL::DsLoadTr4_B64::create(rewriter, loc, rocdlResultType, srcPtr,
2235 /*alias_scopes=*/{},
2236 /*noalias_scopes=*/{}, /*tbaa=*/{})
2237 .getResult();
2238 break;
2239 }
2240 case 6: {
2241 if (numElements != 16)
2242 return emitNumElementsError(16, "gfx1250+");
2243 intrinsic =
2244 ROCDL::DsLoadTr6_B96::create(rewriter, loc, rocdlResultType, srcPtr,
2245 /*alias_scopes=*/{},
2246 /*noalias_scopes=*/{}, /*tbaa=*/{})
2247 .getResult();
2248 break;
2249 }
2250 case 8: {
2251 if (numElements != 8)
2252 return emitNumElementsError(8, "gfx1250+");
2253 intrinsic =
2254 ROCDL::DsLoadTr8_B64::create(rewriter, loc, rocdlResultType, srcPtr,
2255 /*alias_scopes=*/{},
2256 /*noalias_scopes=*/{}, /*tbaa=*/{})
2257 .getResult();
2258 break;
2259 }
2260 case 16: {
2261 if (numElements != 8)
2262 return emitNumElementsError(8, "gfx1250+");
2263 intrinsic = ROCDL::DsLoadTr16_B128::create(
2264 rewriter, loc, rocdlResultType, srcPtr,
2265 /*alias_scopes=*/{}, /*noalias_scopes=*/{}, /*tbaa=*/{})
2266 .getResult();
2267 break;
2268 }
2269 default:
2270 return op.emitOpError("Unsupported element size for transpose load");
2271 }
2272 } else {
2273 switch (elementTypeSize) {
2274 case 4: {
2275 if (numElements != 16)
2276 return emitNumElementsError(16, "gfx950");
2277 intrinsic = ROCDL::ds_read_tr4_b64::create(
2278 rewriter, loc, rocdlResultType, srcPtr,
2279 /*alias_scopes=*/{}, /*noalias_scopes=*/{}, /*tbaa=*/{})
2280 .getResult();
2281 break;
2282 }
2283 case 6: {
2284 if (numElements != 16)
2285 return emitNumElementsError(16, "gfx950");
2286 intrinsic = ROCDL::ds_read_tr6_b96::create(
2287 rewriter, loc, rocdlResultType, srcPtr,
2288 /*alias_scopes=*/{}, /*noalias_scopes=*/{}, /*tbaa=*/{})
2289 .getResult();
2290 break;
2291 }
2292 case 8: {
2293 if (numElements != 8)
2294 return emitNumElementsError(8, "gfx950");
2295 intrinsic = ROCDL::ds_read_tr8_b64::create(
2296 rewriter, loc, rocdlResultType, srcPtr,
2297 /*alias_scopes=*/{}, /*noalias_scopes=*/{}, /*tbaa=*/{})
2298 .getResult();
2299 break;
2300 }
2301 case 16: {
2302 if (numElements != 4)
2303 return emitNumElementsError(4, "gfx950");
2304 intrinsic = ROCDL::ds_read_tr16_b64::create(
2305 rewriter, loc, rocdlResultType, srcPtr,
2306 /*alias_scopes=*/{}, /*noalias_scopes=*/{}, /*tbaa=*/{})
2307 .getResult();
2308 break;
2309 }
2310 default:
2311 return op.emitOpError("Unsupported element size for transpose load");
2312 }
2313 }
2314
2315 assert(intrinsic && "expected ROCDL transpose load intrinsic");
2316 if (intrinsic.getType() == llvmResultType) {
2317 rewriter.replaceOp(op, intrinsic);
2318 return success();
2319 }
2320 rewriter.replaceOpWithNewOp<LLVM::BitcastOp>(op, llvmResultType, intrinsic);
2321 return success();
2322 }
2323};
2324
2325struct GlobalTransposeLoadOpLowering
2326 : public ConvertOpToLLVMPattern<GlobalTransposeLoadOp> {
2327 GlobalTransposeLoadOpLowering(const LLVMTypeConverter &converter,
2328 const ROCDL::TargetInfo &target)
2329 : ConvertOpToLLVMPattern<GlobalTransposeLoadOp>(converter),
2330 target(target) {}
2331
2332 ROCDL::TargetInfo target;
2333
2334 LogicalResult
2335 matchAndRewrite(GlobalTransposeLoadOp op,
2336 GlobalTransposeLoadOpAdaptor adaptor,
2337 ConversionPatternRewriter &rewriter) const override {
2338 if (!target.has(llvm::AMDGPU::FEAT_GFX12_INSTS))
2339 return op.emitOpError(
2340 "global_transpose_load is only supported on gfx1200+");
2341
2342 Location loc = op.getLoc();
2343 auto srcMemRefType = cast<MemRefType>(op.getSrc().getType());
2344 auto resultType = cast<VectorType>(op.getResult().getType());
2345
2346 Value srcPtr = getStridedElementPtr(
2347 rewriter, loc, srcMemRefType, adaptor.getSrc(), adaptor.getSrcIndices(),
2348 LLVM::GEPNoWrapFlags::inbounds | LLVM::GEPNoWrapFlags::nuw);
2349
2350 size_t numElements = resultType.getNumElements();
2351 size_t elementTypeSize =
2352 resultType.getElementType().getIntOrFloatBitWidth();
2353
2354 // ROCDL global transpose load intrinsics return vectors of i32 for
2355 // sub-16-bit elements, matching the LDS lowering convention.
2356 Type rocdlResultType =
2357 elementTypeSize < 16
2358 ? VectorType::get((numElements * elementTypeSize) / 32,
2359 rewriter.getIntegerType(32))
2360 : typeConverter->convertType(resultType);
2361 Type llvmResultType = typeConverter->convertType(resultType);
2362
2363 switch (elementTypeSize) {
2364 case 4: {
2365 assert(numElements == 16);
2366 if (!target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS))
2367 return op.emitOpError("4-bit global_transpose_load requires gfx1250+");
2368 auto rocdlOp = ROCDL::GlobalLoadTr4_B64::create(
2369 rewriter, loc, rocdlResultType, srcPtr, ArrayAttr{}, ArrayAttr{},
2370 ArrayAttr{});
2371 rewriter.replaceOpWithNewOp<LLVM::BitcastOp>(op, llvmResultType, rocdlOp);
2372 break;
2373 }
2374 case 6: {
2375 assert(numElements == 16);
2376 if (!target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS))
2377 return op.emitOpError("6-bit global_transpose_load requires gfx1250+");
2378 auto rocdlOp = ROCDL::GlobalLoadTr6_B96::create(
2379 rewriter, loc, rocdlResultType, srcPtr, ArrayAttr{}, ArrayAttr{},
2380 ArrayAttr{});
2381 rewriter.replaceOpWithNewOp<LLVM::BitcastOp>(op, llvmResultType, rocdlOp);
2382 break;
2383 }
2384 case 8: {
2385 assert(numElements == 8);
2386 auto rocdlOp = ROCDL::GlobalLoadTr8_B64::create(
2387 rewriter, loc, rocdlResultType, srcPtr, ArrayAttr{}, ArrayAttr{},
2388 ArrayAttr{});
2389 rewriter.replaceOpWithNewOp<LLVM::BitcastOp>(op, llvmResultType, rocdlOp);
2390 break;
2391 }
2392 case 16: {
2393 assert(numElements == 8);
2394 rewriter.replaceOpWithNewOp<ROCDL::GlobalLoadTr8_B128>(
2395 op, llvmResultType, srcPtr, ArrayAttr{}, ArrayAttr{}, ArrayAttr{});
2396 break;
2397 }
2398 default:
2399 return op.emitOpError(
2400 "unsupported element size for global transpose load");
2401 }
2402 return success();
2403 }
2404};
2405
2406struct GatherToLDSOpLowering : public ConvertOpToLLVMPattern<GatherToLDSOp> {
2407 GatherToLDSOpLowering(const LLVMTypeConverter &converter,
2408 const ROCDL::TargetInfo &target)
2409 : ConvertOpToLLVMPattern<GatherToLDSOp>(converter), target(target) {}
2410
2411 ROCDL::TargetInfo target;
2412
2413 LogicalResult
2414 matchAndRewrite(GatherToLDSOp op, GatherToLDSOpAdaptor adaptor,
2415 ConversionPatternRewriter &rewriter) const override {
2416 if (!target.has(llvm::AMDGPU::FEAT_VMEM_TO_LDS_LOAD_INSTS))
2417 return op.emitOpError("pre-gfx9 and post-gfx10 not supported");
2418
2419 Location loc = op.getLoc();
2420
2421 auto srcMemRefType = cast<MemRefType>(op.getSrc().getType());
2422 auto dstMemRefType = cast<MemRefType>(op.getDst().getType());
2423
2424 // TODO: instead of only transfering one element per thread, we could
2425 // augment it to transfer multiple elements per thread by issuing multiple
2426 // `global_load_lds` instructions.
2427 Type transferType = op.getTransferType();
2428 int loadWidth = [&]() -> int {
2429 if (auto transferVectorType = dyn_cast<VectorType>(transferType)) {
2430 return (transferVectorType.getNumElements() *
2431 transferVectorType.getElementTypeBitWidth()) /
2432 8;
2433 }
2434 return transferType.getIntOrFloatBitWidth() / 8;
2435 }();
2436
2437 // Currently only 1, 2, 4, 12 and 16 byte loads are supported.
2438 if (!llvm::is_contained({1, 2, 4, 12, 16}, loadWidth))
2439 return op.emitOpError("chipset unsupported element size");
2440
2441 if (!target.has(llvm::AMDGPU::FEAT_GFX950_INSTS) &&
2442 llvm::is_contained({12, 16}, loadWidth))
2443 return op.emitOpError("Gather to LDS instructions with 12-byte and "
2444 "16-byte load widths are only supported on gfx950");
2445
2446 Value srcPtr =
2447 getStridedElementPtr(rewriter, loc, srcMemRefType, adaptor.getSrc(),
2448 (adaptor.getSrcIndices()));
2449 Value dstPtr =
2450 getStridedElementPtr(rewriter, loc, dstMemRefType, adaptor.getDst(),
2451 (adaptor.getDstIndices()));
2452
2453 if (op.getAsync()) {
2454 rewriter.replaceOpWithNewOp<ROCDL::LoadAsyncToLDSOp>(
2455 op, srcPtr, dstPtr, rewriter.getI32IntegerAttr(loadWidth),
2456 /*offset=*/rewriter.getI32IntegerAttr(0),
2457 /*aux=*/rewriter.getI32IntegerAttr(0), ArrayAttr{}, ArrayAttr{},
2458 ArrayAttr{});
2459 } else {
2460 rewriter.replaceOpWithNewOp<ROCDL::LoadToLDSOp>(
2461 op, srcPtr, dstPtr, rewriter.getI32IntegerAttr(loadWidth),
2462 /*offset=*/rewriter.getI32IntegerAttr(0),
2463 /*aux=*/rewriter.getI32IntegerAttr(0), ArrayAttr{}, ArrayAttr{},
2464 ArrayAttr{});
2465 }
2466
2467 return success();
2468 }
2469};
2470
2471struct GlobalLoadAsyncToLDSOpLowering
2472 : public ConvertOpToLLVMPattern<GlobalLoadAsyncToLDSOp> {
2473 GlobalLoadAsyncToLDSOpLowering(const LLVMTypeConverter &converter,
2474 const ROCDL::TargetInfo &target)
2475 : ConvertOpToLLVMPattern<GlobalLoadAsyncToLDSOp>(converter),
2476 target(target) {}
2477
2478 ROCDL::TargetInfo target;
2479
2480 LogicalResult
2481 matchAndRewrite(GlobalLoadAsyncToLDSOp op,
2482 GlobalLoadAsyncToLDSOpAdaptor adaptor,
2483 ConversionPatternRewriter &rewriter) const override {
2484 if (!target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS))
2485 return op.emitOpError(
2486 "global_load_async_to_lds is only supported on gfx1250+");
2487
2488 Location loc = op.getLoc();
2489 auto srcMemRefType = cast<MemRefType>(op.getSrc().getType());
2490 auto dstMemRefType = cast<MemRefType>(op.getDst().getType());
2491
2492 Type transferType = op.getTransferType();
2493 int transferBits =
2494 isa<VectorType>(transferType)
2495 ? cast<VectorType>(transferType).getNumElements() *
2496 cast<VectorType>(transferType).getElementTypeBitWidth()
2497 : transferType.getIntOrFloatBitWidth();
2498
2499 Value srcPtr =
2500 getStridedElementPtr(rewriter, loc, srcMemRefType, adaptor.getSrc(),
2501 adaptor.getSrcIndices());
2502 Value dstPtr =
2503 getStridedElementPtr(rewriter, loc, dstMemRefType, adaptor.getDst(),
2504 adaptor.getDstIndices());
2505
2506 if (op.getMask()) {
2507 Value mask = adaptor.getMask();
2508 int64_t nullptrVal =
2509 llvm::AMDGPU::getNullPointerValue(llvm::AMDGPUAS::LOCAL_ADDRESS);
2510 Value nullInt =
2511 createI32Constant(rewriter, loc, static_cast<int32_t>(nullptrVal));
2512 Value nullPtr =
2513 LLVM::IntToPtrOp::create(rewriter, loc, dstPtr.getType(), nullInt);
2514 dstPtr = LLVM::SelectOp::create(rewriter, loc, mask, dstPtr, nullPtr);
2515 }
2516
2517 auto offset = rewriter.getI32IntegerAttr(0);
2518 Attribute aux = rewriter.getI32IntegerAttr(0);
2519
2520 switch (transferBits) {
2521 case 8:
2522 rewriter.replaceOpWithNewOp<ROCDL::GlobalLoadAsyncToLDSB8Op>(
2523 op, srcPtr, dstPtr, offset, aux, ArrayAttr{}, ArrayAttr{},
2524 ArrayAttr{});
2525 break;
2526 case 32:
2527 rewriter.replaceOpWithNewOp<ROCDL::GlobalLoadAsyncToLDSB32Op>(
2528 op, srcPtr, dstPtr, offset, aux, ArrayAttr{}, ArrayAttr{},
2529 ArrayAttr{});
2530 break;
2531 case 64:
2532 rewriter.replaceOpWithNewOp<ROCDL::GlobalLoadAsyncToLDSB64Op>(
2533 op, srcPtr, dstPtr, offset, aux, ArrayAttr{}, ArrayAttr{},
2534 ArrayAttr{});
2535 break;
2536 case 128:
2537 rewriter.replaceOpWithNewOp<ROCDL::GlobalLoadAsyncToLDSB128Op>(
2538 op, srcPtr, dstPtr, offset, aux, ArrayAttr{}, ArrayAttr{},
2539 ArrayAttr{});
2540 break;
2541 default:
2542 return op.emitOpError("unsupported transfer width");
2543 }
2544 return success();
2545 }
2546};
2547
2548namespace {
2549struct ExtPackedFp8OpLowering final
2550 : public ConvertOpToLLVMPattern<ExtPackedFp8Op> {
2551 ExtPackedFp8OpLowering(const LLVMTypeConverter &converter,
2552 const ROCDL::TargetInfo &target)
2553 : ConvertOpToLLVMPattern<amdgpu::ExtPackedFp8Op>(converter),
2554 target(target) {}
2555 ROCDL::TargetInfo target;
2556
2557 LogicalResult
2558 matchAndRewrite(ExtPackedFp8Op op, ExtPackedFp8OpAdaptor adaptor,
2559 ConversionPatternRewriter &rewriter) const override;
2560};
2561
2562struct ScaledExtPackedMatrixOpLowering final
2563 : public ConvertOpToLLVMPattern<ScaledExtPackedMatrixOp> {
2564 ScaledExtPackedMatrixOpLowering(const LLVMTypeConverter &converter,
2565 const ROCDL::TargetInfo &target)
2566 : ConvertOpToLLVMPattern<amdgpu::ScaledExtPackedMatrixOp>(converter),
2567 target(target) {}
2568 ROCDL::TargetInfo target;
2569
2570 LogicalResult
2571 matchAndRewrite(ScaledExtPackedMatrixOp op,
2572 ScaledExtPackedMatrixOpAdaptor adaptor,
2573 ConversionPatternRewriter &rewriter) const override;
2574};
2575
2576struct PackedTrunc2xFp8OpLowering final
2577 : public ConvertOpToLLVMPattern<PackedTrunc2xFp8Op> {
2578 PackedTrunc2xFp8OpLowering(const LLVMTypeConverter &converter,
2579 const ROCDL::TargetInfo &target)
2580 : ConvertOpToLLVMPattern<amdgpu::PackedTrunc2xFp8Op>(converter),
2581 target(target) {}
2582 ROCDL::TargetInfo target;
2583
2584 LogicalResult
2585 matchAndRewrite(PackedTrunc2xFp8Op op, PackedTrunc2xFp8OpAdaptor adaptor,
2586 ConversionPatternRewriter &rewriter) const override;
2587};
2588
2589struct PackedStochRoundFp8OpLowering final
2590 : public ConvertOpToLLVMPattern<PackedStochRoundFp8Op> {
2591 PackedStochRoundFp8OpLowering(const LLVMTypeConverter &converter,
2592 const ROCDL::TargetInfo &target)
2593 : ConvertOpToLLVMPattern<amdgpu::PackedStochRoundFp8Op>(converter),
2594 target(target) {}
2595 ROCDL::TargetInfo target;
2596
2597 LogicalResult
2598 matchAndRewrite(PackedStochRoundFp8Op op,
2599 PackedStochRoundFp8OpAdaptor adaptor,
2600 ConversionPatternRewriter &rewriter) const override;
2601};
2602
2603struct ScaledExtPackedOpLowering final
2604 : public ConvertOpToLLVMPattern<ScaledExtPackedOp> {
2605 ScaledExtPackedOpLowering(const LLVMTypeConverter &converter,
2606 const ROCDL::TargetInfo &target)
2607 : ConvertOpToLLVMPattern<amdgpu::ScaledExtPackedOp>(converter),
2608 target(target) {}
2609 ROCDL::TargetInfo target;
2610
2611 LogicalResult
2612 matchAndRewrite(ScaledExtPackedOp op, ScaledExtPackedOpAdaptor adaptor,
2613 ConversionPatternRewriter &rewriter) const override;
2614};
2615
2616struct PackedScaledTruncOpLowering final
2617 : public ConvertOpToLLVMPattern<PackedScaledTruncOp> {
2618 PackedScaledTruncOpLowering(const LLVMTypeConverter &converter,
2619 const ROCDL::TargetInfo &target)
2620 : ConvertOpToLLVMPattern<amdgpu::PackedScaledTruncOp>(converter),
2621 target(target) {}
2622 ROCDL::TargetInfo target;
2623
2624 LogicalResult
2625 matchAndRewrite(PackedScaledTruncOp op, PackedScaledTruncOpAdaptor adaptor,
2626 ConversionPatternRewriter &rewriter) const override;
2627};
2628
2629} // end namespace
2630
2631LogicalResult ExtPackedFp8OpLowering::matchAndRewrite(
2632 ExtPackedFp8Op op, ExtPackedFp8OpAdaptor adaptor,
2633 ConversionPatternRewriter &rewriter) const {
2634 Location loc = op.getLoc();
2635 if (!target.has(llvm::AMDGPU::FEAT_FP8_CONVERSION_INSTS))
2636 return rewriter.notifyMatchFailure(
2637 loc, "Fp8 conversion instructions are not available on target "
2638 "architecture and their emulation is not implemented");
2639 Type v4i8 =
2640 getTypeConverter()->convertType(VectorType::get(4, rewriter.getI8Type()));
2641 Type i32 = getTypeConverter()->convertType(rewriter.getI32Type());
2642 Type f32 = getTypeConverter()->convertType(op.getResult().getType());
2643
2644 Value source = adaptor.getSource();
2645 auto sourceVecType = dyn_cast<VectorType>(op.getSource().getType());
2646 auto resultVecType = dyn_cast<VectorType>(op.getResult().getType());
2647 Type sourceElemType = getElementTypeOrSelf(op.getSource());
2648 // Extend to a v4i8
2649 if (!sourceVecType || sourceVecType.getNumElements() < 4) {
2650 Value longVec = LLVM::UndefOp::create(rewriter, loc, v4i8);
2651 if (!sourceVecType) {
2652 longVec = LLVM::InsertElementOp::create(
2653 rewriter, loc, longVec, source, createI32Constant(rewriter, loc, 0));
2654 } else {
2655 for (int32_t i = 0, e = sourceVecType.getNumElements(); i < e; ++i) {
2656 Value idx = createI32Constant(rewriter, loc, i);
2657 Value elem = LLVM::ExtractElementOp::create(rewriter, loc, source, idx);
2658 longVec =
2659 LLVM::InsertElementOp::create(rewriter, loc, longVec, elem, idx);
2660 }
2661 }
2662 source = longVec;
2663 }
2664 Value i32Source = LLVM::BitcastOp::create(rewriter, loc, i32, source);
2665 if (resultVecType) {
2666 if (typeIsExpectedBf8ForTarget(target, sourceElemType)) {
2667 rewriter.replaceOpWithNewOp<ROCDL::CvtPkF32Bf8Op>(op, f32, i32Source,
2668 op.getIndex());
2669 } else if (typeIsExpectedFp8ForTarget(target, sourceElemType)) {
2670 rewriter.replaceOpWithNewOp<ROCDL::CvtPkF32Fp8Op>(op, f32, i32Source,
2671 op.getIndex());
2672 }
2673 } else {
2674 if (typeIsExpectedBf8ForTarget(target, sourceElemType)) {
2675 rewriter.replaceOpWithNewOp<ROCDL::CvtF32Bf8Op>(op, f32, i32Source,
2676 op.getIndex());
2677 } else if (typeIsExpectedFp8ForTarget(target, sourceElemType)) {
2678 rewriter.replaceOpWithNewOp<ROCDL::CvtF32Fp8Op>(op, f32, i32Source,
2679 op.getIndex());
2680 }
2681 }
2682 return success();
2683}
2684
2685int32_t getScaleSel(int32_t blockSize, unsigned bitWidth, int32_t scaleWaveHalf,
2686 int32_t firstScaleByte) {
2687 // When lowering amdgpu.scaled_ext_packed_matrix to rocdl.cvt.scale.pk*.f*.f*
2688 // operations, the attributes blockSize, sourceType, scaleWaveHalf, and
2689 // firstScaleByte are merged into a single attribute scaleSel. This is how
2690 // those values are merged together. (Note: scaleWaveHalf isn't a high-level
2691 // attribute but is derifed from firstScaleLane).
2692 assert(llvm::is_contained({16, 32}, blockSize));
2693 assert(llvm::is_contained({4u, 6u, 8u}, bitWidth));
2694
2695 const bool isFp8 = bitWidth == 8;
2696 const bool isBlock16 = blockSize == 16;
2697
2698 if (!isFp8) {
2699 int32_t bit0 = isBlock16;
2700 assert(llvm::is_contained({0, 1, 2}, firstScaleByte));
2701 int32_t bit1 = (firstScaleByte == 2) << 1;
2702 assert(llvm::is_contained({0, 1}, scaleWaveHalf));
2703 int32_t bit2 = scaleWaveHalf << 2;
2704 return bit2 | bit1 | bit0;
2705 }
2706
2707 int32_t bit0 = isBlock16;
2708 // firstScaleByte is guaranteed to be defined by two bits.
2709 assert(llvm::is_contained({0, 1, 2, 3}, firstScaleByte));
2710 int32_t bits2and1 = firstScaleByte << 1;
2711 assert(llvm::is_contained({0, 1}, scaleWaveHalf));
2712 int32_t bit3 = scaleWaveHalf << 3;
2713 int32_t bits = bit3 | bits2and1 | bit0;
2714 // These are invalid cases.
2715 assert(!llvm::is_contained(
2716 {0b0011, 0b0101, 0b0111, 0b1000, 0b1001, 0b1011, 0b1111}, bits));
2717 return bits;
2718}
2719
2720static std::optional<StringRef>
2721scaledExtPacked816ToIntrinsic(Type srcElemType, Type destElemType) {
2722 using fp4 = Float4E2M1FNType;
2723 using fp8 = Float8E4M3FNType;
2724 using bf8 = Float8E5M2Type;
2725 using fp6 = Float6E2M3FNType;
2726 using bf6 = Float6E3M2FNType;
2727 if (isa<fp4>(srcElemType)) {
2728 if (destElemType.isF16())
2729 return ROCDL::CvtPkScalePk8F16Fp4Op::getOperationName();
2730 if (destElemType.isBF16())
2731 return ROCDL::CvtPkScalePk8Bf16Fp4Op::getOperationName();
2732 if (destElemType.isF32())
2733 return ROCDL::CvtPkScalePk8F32Fp4Op::getOperationName();
2734 return std::nullopt;
2735 }
2736 if (isa<fp8>(srcElemType)) {
2737 if (destElemType.isF16())
2738 return ROCDL::CvtPkScalePk8F16Fp8Op::getOperationName();
2739 if (destElemType.isBF16())
2740 return ROCDL::CvtPkScalePk8Bf16Fp8Op::getOperationName();
2741 if (destElemType.isF32())
2742 return ROCDL::CvtPkScalePk8F32Fp8Op::getOperationName();
2743 return std::nullopt;
2744 }
2745 if (isa<bf8>(srcElemType)) {
2746 if (destElemType.isF16())
2747 return ROCDL::CvtPkScalePk8F16Bf8Op::getOperationName();
2748 if (destElemType.isBF16())
2749 return ROCDL::CvtPkScalePk8Bf16Bf8Op::getOperationName();
2750 if (destElemType.isF32())
2751 return ROCDL::CvtPkScalePk8F32Bf8Op::getOperationName();
2752 return std::nullopt;
2753 }
2754 if (isa<fp6>(srcElemType)) {
2755 if (destElemType.isF16())
2756 return ROCDL::CvtPkScalePk16F16Fp6Op::getOperationName();
2757 if (destElemType.isBF16())
2758 return ROCDL::CvtPkScalePk16Bf16Fp6Op::getOperationName();
2759 if (destElemType.isF32())
2760 return ROCDL::CvtPkScalePk16F32Fp6Op::getOperationName();
2761 return std::nullopt;
2762 }
2763 if (isa<bf6>(srcElemType)) {
2764 if (destElemType.isF16())
2765 return ROCDL::CvtPkScalePk16F16Bf6Op::getOperationName();
2766 if (destElemType.isBF16())
2767 return ROCDL::CvtPkScalePk16Bf16Bf6Op::getOperationName();
2768 if (destElemType.isF32())
2769 return ROCDL::CvtPkScalePk16F32Bf6Op::getOperationName();
2770 return std::nullopt;
2771 }
2772 llvm_unreachable("invalid combination of element types for packed conversion "
2773 "instructions");
2774}
2775
2776LogicalResult ScaledExtPackedMatrixOpLowering::matchAndRewrite(
2777 ScaledExtPackedMatrixOp op, ScaledExtPackedMatrixOpAdaptor adaptor,
2778 ConversionPatternRewriter &rewriter) const {
2779 using fp4 = Float4E2M1FNType;
2780 using fp8 = Float8E4M3FNType;
2781 using bf8 = Float8E5M2Type;
2782 using fp6 = Float6E2M3FNType;
2783 using bf6 = Float6E3M2FNType;
2784 Location loc = op.getLoc();
2785 if (!target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS)) {
2786 return rewriter.notifyMatchFailure(
2787 loc,
2788 "Scaled fp packed conversion instructions are not available on target "
2789 "architecture and their emulation is not implemented");
2790 }
2791 // Convert user-facing firstScaleLane (0 or 16) to the half of the wave that
2792 // is being selected.
2793 int32_t scaleWaveHalf = op.getFirstScaleLane() / 16;
2794 int32_t firstScaleByte = op.getFirstScaleByte();
2795 int32_t blockSize = op.getBlockSize();
2796 auto sourceType = cast<VectorType>(op.getSource().getType());
2797 auto srcElemType = cast<FloatType>(sourceType.getElementType());
2798 unsigned bitWidth = srcElemType.getWidth();
2799
2800 auto targetType = cast<VectorType>(op.getResult().getType());
2801 auto destElemType = cast<FloatType>(targetType.getElementType());
2802
2803 IntegerType i32 = rewriter.getI32Type();
2804 Value source = adaptor.getSource();
2805 Type llvmResultType = typeConverter->convertType(op.getResult().getType());
2806 Type packedType = nullptr;
2807 if (isa<fp4>(srcElemType)) {
2808 packedType = i32;
2809 packedType = getTypeConverter()->convertType(packedType);
2810 } else if (isa<fp8, bf8>(srcElemType)) {
2811 packedType = VectorType::get(2, i32);
2812 packedType = getTypeConverter()->convertType(packedType);
2813 } else if (isa<fp6, bf6>(srcElemType)) {
2814 packedType = VectorType::get(3, i32);
2815 packedType = getTypeConverter()->convertType(packedType);
2816 } else {
2817 llvm_unreachable("invalid element type for packed scaled ext");
2818 }
2819
2820 if (!packedType || !llvmResultType) {
2821 return rewriter.notifyMatchFailure(op, "type conversion failed");
2822 }
2823
2824 std::optional<StringRef> maybeIntrinsic =
2825 scaledExtPacked816ToIntrinsic(srcElemType, destElemType);
2826 if (!maybeIntrinsic.has_value())
2827 return op.emitOpError(
2828 "no intrinsic matching packed scaled conversion on the given chipset");
2829
2830 int32_t scaleSel =
2831 getScaleSel(blockSize, bitWidth, scaleWaveHalf, firstScaleByte);
2832 Value castedScale =
2833 LLVM::BitcastOp::create(rewriter, loc, i32, adaptor.getScale());
2834 Value castedSource =
2835 LLVM::BitcastOp::create(rewriter, loc, packedType, source);
2836
2837 OperationState loweredOp(loc, *maybeIntrinsic);
2838 loweredOp.addTypes({llvmResultType});
2839 loweredOp.addOperands({castedSource, castedScale});
2840
2841 SmallVector<NamedAttribute, 1> attrs;
2842 attrs.push_back(
2843 NamedAttribute("scaleSel", rewriter.getI32IntegerAttr(scaleSel)));
2844
2845 loweredOp.addAttributes(attrs);
2846 Operation *lowered = rewriter.create(loweredOp);
2847 rewriter.replaceOp(op, lowered);
2848
2849 return success();
2850}
2851
2852LogicalResult ScaledExtPackedOpLowering::matchAndRewrite(
2853 ScaledExtPackedOp op, ScaledExtPackedOpAdaptor adaptor,
2854 ConversionPatternRewriter &rewriter) const {
2855 Location loc = op.getLoc();
2856 if (!target.has(llvm::AMDGPU::FEAT_GFX950_INSTS))
2857 return rewriter.notifyMatchFailure(
2858 loc, "Scaled fp conversion instructions are not available on target "
2859 "architecture and their emulation is not implemented");
2860 Type i32 = getTypeConverter()->convertType(rewriter.getI32Type());
2861
2862 Value source = adaptor.getSource();
2863 Value scale = adaptor.getScale();
2864
2865 VectorType sourceVecType = cast<VectorType>(op.getSource().getType());
2866 Type sourceElemType = sourceVecType.getElementType();
2867 VectorType destVecType = cast<VectorType>(op.getResult().getType());
2868 Type destElemType = destVecType.getElementType();
2869
2870 VectorType packedVecType;
2871 if (isa<Float8E5M2Type, Float8E4M3FNType>(sourceElemType)) {
2872 VectorType v4i8 = VectorType::get(4, rewriter.getI8Type());
2873 packedVecType = cast<VectorType>(getTypeConverter()->convertType(v4i8));
2874 } else if (isa<Float4E2M1FNType>(sourceElemType)) {
2875 VectorType v8i4 = VectorType::get(8, rewriter.getI4Type());
2876 packedVecType = cast<VectorType>(getTypeConverter()->convertType(v8i4));
2877 } else {
2878 llvm_unreachable("invalid element type for scaled ext");
2879 }
2880
2881 // Extend to a packedVectorType
2882 if (sourceVecType.getNumElements() < packedVecType.getNumElements()) {
2883 Value longVec = LLVM::ZeroOp::create(rewriter, loc, packedVecType);
2884 if (!sourceVecType) {
2885 longVec = LLVM::InsertElementOp::create(
2886 rewriter, loc, longVec, source, createI32Constant(rewriter, loc, 0));
2887 } else {
2888 for (int32_t i = 0, e = sourceVecType.getNumElements(); i < e; ++i) {
2889 Value idx = createI32Constant(rewriter, loc, i);
2890 Value elem = LLVM::ExtractElementOp::create(rewriter, loc, source, idx);
2891 longVec =
2892 LLVM::InsertElementOp::create(rewriter, loc, longVec, elem, idx);
2893 }
2894 }
2895 source = longVec;
2896 }
2897 Value i32Source = LLVM::BitcastOp::create(rewriter, loc, i32, source);
2898
2899 if (isa<Float8E5M2Type>(sourceElemType) && destElemType.isF32())
2900 rewriter.replaceOpWithNewOp<ROCDL::CvtScaleF32PkF32Bf8Op>(
2901 op, destVecType, i32Source, scale, op.getIndex());
2902 else if (isa<Float8E5M2Type>(sourceElemType) && destElemType.isF16())
2903 rewriter.replaceOpWithNewOp<ROCDL::CvtScaleF32PkF16Bf8Op>(
2904 op, destVecType, i32Source, scale, op.getIndex());
2905 else if (isa<Float8E5M2Type>(sourceElemType) && destElemType.isBF16())
2906 rewriter.replaceOpWithNewOp<ROCDL::CvtScaleF32PkBf16Bf8Op>(
2907 op, destVecType, i32Source, scale, op.getIndex());
2908 else if (isa<Float8E4M3FNType>(sourceElemType) && destElemType.isF32())
2909 rewriter.replaceOpWithNewOp<ROCDL::CvtScaleF32PkF32Fp8Op>(
2910 op, destVecType, i32Source, scale, op.getIndex());
2911 else if (isa<Float8E4M3FNType>(sourceElemType) && destElemType.isF16())
2912 rewriter.replaceOpWithNewOp<ROCDL::CvtScaleF32PkF16Fp8Op>(
2913 op, destVecType, i32Source, scale, op.getIndex());
2914 else if (isa<Float8E4M3FNType>(sourceElemType) && destElemType.isBF16())
2915 rewriter.replaceOpWithNewOp<ROCDL::CvtScaleF32PkBf16Fp8Op>(
2916 op, destVecType, i32Source, scale, op.getIndex());
2917 else if (isa<Float4E2M1FNType>(sourceElemType) && destElemType.isF32())
2918 rewriter.replaceOpWithNewOp<ROCDL::CvtScaleF32PkF32Fp4Op>(
2919 op, destVecType, i32Source, scale, op.getIndex());
2920 else if (isa<Float4E2M1FNType>(sourceElemType) && destElemType.isF16())
2921 rewriter.replaceOpWithNewOp<ROCDL::CvtScaleF32PkF16Fp4Op>(
2922 op, destVecType, i32Source, scale, op.getIndex());
2923 else if (isa<Float4E2M1FNType>(sourceElemType) && destElemType.isBF16())
2924 rewriter.replaceOpWithNewOp<ROCDL::CvtScaleF32PkBf16Fp4Op>(
2925 op, destVecType, i32Source, scale, op.getIndex());
2926 else
2927 return failure();
2928
2929 return success();
2930}
2931
2932LogicalResult PackedScaledTruncOpLowering::matchAndRewrite(
2933 PackedScaledTruncOp op, PackedScaledTruncOpAdaptor adaptor,
2934 ConversionPatternRewriter &rewriter) const {
2935 Location loc = op.getLoc();
2936 if (!target.has(llvm::AMDGPU::FEAT_GFX950_INSTS))
2937 return rewriter.notifyMatchFailure(
2938 loc, "Scaled fp conversion instructions are not available on target "
2939 "architecture and their emulation is not implemented");
2940 Type v2i16 = getTypeConverter()->convertType(
2941 VectorType::get(2, rewriter.getI16Type()));
2942 Type i32 = getTypeConverter()->convertType(rewriter.getI32Type());
2943
2944 Type resultType = op.getResult().getType();
2945 Type resultElemType = getElementTypeOrSelf(resultType);
2946 VectorType sourceVecType = cast<VectorType>(op.getSource().getType());
2947 Type sourceElemType = sourceVecType.getElementType();
2948
2949 Type intResultType = isa<Float4E2M1FNType>(resultElemType) ? i32 : v2i16;
2950
2951 Value source = adaptor.getSource();
2952 Value scale = adaptor.getScale();
2953 Value existing = adaptor.getExisting();
2954 if (existing)
2955 existing = LLVM::BitcastOp::create(rewriter, loc, intResultType, existing);
2956 else
2957 existing = LLVM::ZeroOp::create(rewriter, loc, intResultType);
2958
2959 if (sourceVecType.getNumElements() < 2) {
2960 Value c0 = createI32Constant(rewriter, loc, 0);
2961 Value elem0 = LLVM::ExtractElementOp::create(rewriter, loc, source, c0);
2962 VectorType v2 = VectorType::get(2, sourceElemType);
2963 source = LLVM::ZeroOp::create(rewriter, loc, v2);
2964 source = LLVM::InsertElementOp::create(rewriter, loc, source, elem0, c0);
2965 }
2966
2967 Value sourceA, sourceB;
2968 if (sourceElemType.isF32()) {
2969 Value c0 = createI32Constant(rewriter, loc, 0);
2970 Value c1 = createI32Constant(rewriter, loc, 1);
2971 sourceA = LLVM::ExtractElementOp::create(rewriter, loc, source, c0);
2972 sourceB = LLVM::ExtractElementOp::create(rewriter, loc, source, c1);
2973 }
2974
2975 Value result;
2976 if (sourceElemType.isF32() && isa<Float8E5M2Type>(resultElemType))
2977 result = ROCDL::CvtScaleF32PkBf8F32Op::create(rewriter, loc, intResultType,
2978 existing, sourceA, sourceB,
2979 scale, op.getIndex());
2980 else if (sourceElemType.isF16() && isa<Float8E5M2Type>(resultElemType))
2981 result = ROCDL::CvtScaleF32PkBf8F16Op::create(
2982 rewriter, loc, intResultType, existing, source, scale, op.getIndex());
2983 else if (sourceElemType.isBF16() && isa<Float8E5M2Type>(resultElemType))
2984 result = ROCDL::CvtScaleF32PkBf8Bf16Op::create(
2985 rewriter, loc, intResultType, existing, source, scale, op.getIndex());
2986 else if (sourceElemType.isF32() && isa<Float8E4M3FNType>(resultElemType))
2987 result = ROCDL::CvtScaleF32PkFp8F32Op::create(rewriter, loc, intResultType,
2988 existing, sourceA, sourceB,
2989 scale, op.getIndex());
2990 else if (sourceElemType.isF16() && isa<Float8E4M3FNType>(resultElemType))
2991 result = ROCDL::CvtScaleF32PkFp8F16Op::create(
2992 rewriter, loc, intResultType, existing, source, scale, op.getIndex());
2993 else if (sourceElemType.isBF16() && isa<Float8E4M3FNType>(resultElemType))
2994 result = ROCDL::CvtScaleF32PkFp8Bf16Op::create(
2995 rewriter, loc, intResultType, existing, source, scale, op.getIndex());
2996 else if (sourceElemType.isF32() && isa<Float4E2M1FNType>(resultElemType))
2997 result = ROCDL::CvtScaleF32PkFp4F32Op::create(rewriter, loc, intResultType,
2998 existing, sourceA, sourceB,
2999 scale, op.getIndex());
3000 else if (sourceElemType.isF16() && isa<Float4E2M1FNType>(resultElemType))
3001 result = ROCDL::CvtScaleF32PkFp4F16Op::create(
3002 rewriter, loc, intResultType, existing, source, scale, op.getIndex());
3003 else if (sourceElemType.isBF16() && isa<Float4E2M1FNType>(resultElemType))
3004 result = ROCDL::CvtScaleF32PkFp4Bf16Op::create(
3005 rewriter, loc, intResultType, existing, source, scale, op.getIndex());
3006 else
3007 return failure();
3008
3009 result = rewriter.replaceOpWithNewOp<LLVM::BitcastOp>(
3010 op, getTypeConverter()->convertType(resultType), result);
3011 return success();
3012}
3013
3014LogicalResult PackedTrunc2xFp8OpLowering::matchAndRewrite(
3015 PackedTrunc2xFp8Op op, PackedTrunc2xFp8OpAdaptor adaptor,
3016 ConversionPatternRewriter &rewriter) const {
3017 Location loc = op.getLoc();
3018 if (!target.has(llvm::AMDGPU::FEAT_FP8_CONVERSION_INSTS))
3019 return rewriter.notifyMatchFailure(
3020 loc, "Fp8 conversion instructions are not available on target "
3021 "architecture and their emulation is not implemented");
3022 Type i32 = getTypeConverter()->convertType(rewriter.getI32Type());
3023
3024 Type resultType = op.getResult().getType();
3025 Type resultElemType = getElementTypeOrSelf(resultType);
3026
3027 Value sourceA = adaptor.getSourceA();
3028 Value sourceB = adaptor.getSourceB();
3029 if (!sourceB)
3030 sourceB = LLVM::UndefOp::create(rewriter, loc, sourceA.getType());
3031 Value existing = adaptor.getExisting();
3032 if (existing)
3033 existing = LLVM::BitcastOp::create(rewriter, loc, i32, existing);
3034 else
3035 existing = LLVM::UndefOp::create(rewriter, loc, i32);
3036
3037 Value result;
3038 if (typeIsExpectedBf8ForTarget(target, resultElemType))
3039 result = ROCDL::CvtPkBf8F32Op::create(rewriter, loc, i32, sourceA, sourceB,
3040 existing, op.getWordIndex());
3041 else if (typeIsExpectedFp8ForTarget(target, resultElemType))
3042 result = ROCDL::CvtPkFp8F32Op::create(rewriter, loc, i32, sourceA, sourceB,
3043 existing, op.getWordIndex());
3044 else
3045 return op.emitOpError(
3046 "no truncation to result type available on given chipset");
3047
3048 result = rewriter.replaceOpWithNewOp<LLVM::BitcastOp>(
3049 op, getTypeConverter()->convertType(resultType), result);
3050 return success();
3051}
3052
3053LogicalResult PackedStochRoundFp8OpLowering::matchAndRewrite(
3054 PackedStochRoundFp8Op op, PackedStochRoundFp8OpAdaptor adaptor,
3055 ConversionPatternRewriter &rewriter) const {
3056 Location loc = op.getLoc();
3057 if (!target.has(llvm::AMDGPU::FEAT_FP8_CONVERSION_INSTS))
3058 return rewriter.notifyMatchFailure(
3059 loc, "Fp8 conversion instructions are not available on target "
3060 "architecture and their emulation is not implemented");
3061 Type i32 = getTypeConverter()->convertType(rewriter.getI32Type());
3062
3063 Type resultType = op.getResult().getType();
3064 Type resultElemType = getElementTypeOrSelf(resultType);
3065
3066 Value source = adaptor.getSource();
3067 Value stoch = adaptor.getStochiasticParam();
3068 Value existing = adaptor.getExisting();
3069 if (existing)
3070 existing = LLVM::BitcastOp::create(rewriter, loc, i32, existing);
3071 else
3072 existing = LLVM::UndefOp::create(rewriter, loc, i32);
3073
3074 Value result;
3075 if (typeIsExpectedBf8ForTarget(target, resultElemType))
3076 result = ROCDL::CvtSrBf8F32Op::create(rewriter, loc, i32, source, stoch,
3077 existing, op.getStoreIndex());
3078 else if (typeIsExpectedFp8ForTarget(target, resultElemType))
3079 result = ROCDL::CvtSrFp8F32Op::create(rewriter, loc, i32, source, stoch,
3080 existing, op.getStoreIndex());
3081 else
3082 return op.emitOpError(
3083 "no stochastic rounding to result type available on given chipset");
3084
3085 result = rewriter.replaceOpWithNewOp<LLVM::BitcastOp>(
3086 op, getTypeConverter()->convertType(resultType), result);
3087 return success();
3088}
3089
3090// Implement the AMDGPU_DPPLowering class that will convert the amdgpu.dpp
3091// operation into the corresponding ROCDL instructions.
3092struct AMDGPUDPPLowering : public ConvertOpToLLVMPattern<DPPOp> {
3093 AMDGPUDPPLowering(const LLVMTypeConverter &converter,
3094 const ROCDL::TargetInfo &target)
3095 : ConvertOpToLLVMPattern<DPPOp>(converter), target(target) {}
3096 ROCDL::TargetInfo target;
3097
3098 LogicalResult
3099 matchAndRewrite(DPPOp DppOp, DPPOp::Adaptor adaptor,
3100 ConversionPatternRewriter &rewriter) const override {
3101
3102 // Convert the source operand to the corresponding LLVM type
3103 Location loc = DppOp.getLoc();
3104 Value src = adaptor.getSrc();
3105 Value old = adaptor.getOld();
3106 Type srcType = src.getType();
3107 Type oldType = old.getType();
3108 Type llvmType = nullptr;
3109 if (srcType.getIntOrFloatBitWidth() < 32) {
3110 llvmType = rewriter.getI32Type();
3111 } else if (isa<FloatType>(srcType)) {
3112 llvmType = (srcType.getIntOrFloatBitWidth() == 32)
3113 ? rewriter.getF32Type()
3114 : rewriter.getF64Type();
3115 } else if (isa<IntegerType>(srcType)) {
3116 llvmType = (srcType.getIntOrFloatBitWidth() == 32)
3117 ? rewriter.getI32Type()
3118 : rewriter.getI64Type();
3119 }
3120 auto llvmSrcIntType = typeConverter->convertType(
3121 rewriter.getIntegerType(srcType.getIntOrFloatBitWidth()));
3122
3123 // If the source type is less of 32, use bitcast to convert it to i32.
3124 auto convertOperand = [&](Value operand, Type operandType) {
3125 if (operandType.getIntOrFloatBitWidth() <= 16) {
3126 if (llvm::isa<FloatType>(operandType)) {
3127 operand =
3128 LLVM::BitcastOp::create(rewriter, loc, llvmSrcIntType, operand);
3129 }
3130 auto llvmVecType = typeConverter->convertType(mlir::VectorType::get(
3131 32 / operandType.getIntOrFloatBitWidth(), llvmSrcIntType));
3132 Value undefVec = LLVM::UndefOp::create(rewriter, loc, llvmVecType);
3133 operand =
3134 LLVM::InsertElementOp::create(rewriter, loc, undefVec, operand,
3135 createI32Constant(rewriter, loc, 0));
3136 operand = LLVM::BitcastOp::create(rewriter, loc, llvmType, operand);
3137 }
3138 return operand;
3139 };
3140
3141 src = convertOperand(src, srcType);
3142 old = convertOperand(old, oldType);
3143
3144 // This is taken from the following file llvm/lib/Target/AMDGPU/SIDefines.h
3145 enum DppCtrl : unsigned {
3146 ROW_SHL0 = 0x100,
3147 ROW_SHR0 = 0x110,
3148 ROW_ROR0 = 0x120,
3149 WAVE_SHL1 = 0x130,
3150 WAVE_ROL1 = 0x134,
3151 WAVE_SHR1 = 0x138,
3152 WAVE_ROR1 = 0x13C,
3153 ROW_MIRROR = 0x140,
3154 ROW_HALF_MIRROR = 0x141,
3155 BCAST15 = 0x142,
3156 BCAST31 = 0x143,
3157 };
3158
3159 auto kind = DppOp.getKind();
3160 auto permArgument = DppOp.getPermArgument();
3161 uint32_t DppCtrl = 0;
3162
3163 switch (kind) {
3164
3165 case DPPPerm::quad_perm: {
3166 auto quadPermAttr = cast<ArrayAttr>(*permArgument);
3167 int32_t i = 0;
3168 for (auto elem : quadPermAttr.getAsRange<IntegerAttr>()) {
3169 uint32_t num = elem.getInt();
3170 DppCtrl |= num << (i * 2);
3171 i++;
3172 }
3173 break;
3174 }
3175 case DPPPerm::row_shl: {
3176 auto intAttr = cast<IntegerAttr>(*permArgument);
3177 DppCtrl = intAttr.getInt() + DppCtrl::ROW_SHL0;
3178 break;
3179 }
3180 case DPPPerm::row_shr: {
3181 auto intAttr = cast<IntegerAttr>(*permArgument);
3182 DppCtrl = intAttr.getInt() + DppCtrl::ROW_SHR0;
3183 break;
3184 }
3185 case DPPPerm::row_ror: {
3186 auto intAttr = cast<IntegerAttr>(*permArgument);
3187 DppCtrl = intAttr.getInt() + DppCtrl::ROW_ROR0;
3188 break;
3189 }
3190 case DPPPerm::wave_shl:
3191 DppCtrl = DppCtrl::WAVE_SHL1;
3192 break;
3193 case DPPPerm::wave_shr:
3194 DppCtrl = DppCtrl::WAVE_SHR1;
3195 break;
3196 case DPPPerm::wave_rol:
3197 DppCtrl = DppCtrl::WAVE_ROL1;
3198 break;
3199 case DPPPerm::wave_ror:
3200 DppCtrl = DppCtrl::WAVE_ROR1;
3201 break;
3202 case DPPPerm::row_mirror:
3203 DppCtrl = DppCtrl::ROW_MIRROR;
3204 break;
3205 case DPPPerm::row_half_mirror:
3206 DppCtrl = DppCtrl::ROW_HALF_MIRROR;
3207 break;
3208 case DPPPerm::row_bcast_15:
3209 DppCtrl = DppCtrl::BCAST15;
3210 break;
3211 case DPPPerm::row_bcast_31:
3212 DppCtrl = DppCtrl::BCAST31;
3213 break;
3214 }
3215
3216 // Check for row_mask, bank_mask, bound_ctrl if they exist and create
3217 // constants
3218 auto rowMask = DppOp.getRowMask();
3219 auto bankMask = DppOp.getBankMask();
3220 bool boundCtrl = DppOp.getBoundCtrl();
3221
3222 // create a ROCDL_DPPMovOp instruction with the appropriate attributes
3223 auto dppMovOp =
3224 ROCDL::DPPUpdateOp::create(rewriter, loc, llvmType, old, src, DppCtrl,
3225 rowMask, bankMask, boundCtrl);
3226
3227 Value result = dppMovOp.getRes();
3228 if (srcType.getIntOrFloatBitWidth() < 32) {
3229 result = LLVM::TruncOp::create(rewriter, loc, llvmSrcIntType, result);
3230 if (!llvm::isa<IntegerType>(srcType)) {
3231 result = LLVM::BitcastOp::create(rewriter, loc, srcType, result);
3232 }
3233 }
3234
3235 // We are replacing the AMDGPU_DPPOp instruction with the new
3236 // ROCDL_DPPMovOp instruction
3237 rewriter.replaceOp(DppOp, ValueRange(result));
3238 return success();
3239 }
3240};
3241
3242struct AMDGPUSwizzleBitModeLowering
3243 : public ConvertOpToLLVMPattern<SwizzleBitModeOp> {
3245
3246 LogicalResult
3247 matchAndRewrite(SwizzleBitModeOp op, OpAdaptor adaptor,
3248 ConversionPatternRewriter &rewriter) const override {
3249 Location loc = op.getLoc();
3250 Type i32 = rewriter.getI32Type();
3251 Value src = adaptor.getSrc();
3252 SmallVector<Value> decomposed;
3253 if (failed(LLVM::decomposeValue(rewriter, loc, src, i32, decomposed)))
3254 return rewriter.notifyMatchFailure(op,
3255 "failed to decompose value to i32");
3256 unsigned andMask = op.getAndMask();
3257 unsigned orMask = op.getOrMask();
3258 unsigned xorMask = op.getXorMask();
3259
3260 // bit 15 is 0 for the BitMode swizzle.
3261 // https://gpuopen.com/learn/amd-gcn-assembly-cross-lane-operations/
3262 unsigned mask = andMask | (orMask << 5) | (xorMask << 10);
3263 Value maskValue = createI32Constant(rewriter, loc, mask);
3264 SmallVector<Value> swizzled;
3265 for (Value v : decomposed) {
3266 Value res =
3267 ROCDL::DsSwizzleOp::create(rewriter, loc, v.getType(), v, maskValue);
3268 swizzled.emplace_back(res);
3269 }
3270
3271 Value result = LLVM::composeValue(rewriter, loc, swizzled, src.getType());
3272 rewriter.replaceOp(op, result);
3273 return success();
3274 }
3275};
3276
3277struct AMDGPUPermlaneLowering : public ConvertOpToLLVMPattern<PermlaneSwapOp> {
3279
3280 AMDGPUPermlaneLowering(const LLVMTypeConverter &converter,
3281 const ROCDL::TargetInfo &target)
3282 : ConvertOpToLLVMPattern<PermlaneSwapOp>(converter), target(target) {}
3283 ROCDL::TargetInfo target;
3284
3285 LogicalResult
3286 matchAndRewrite(PermlaneSwapOp op, OpAdaptor adaptor,
3287 ConversionPatternRewriter &rewriter) const override {
3288 unsigned rowLength = op.getRowLength();
3289 bool supported = rowLength == 16
3290 ? target.has(llvm::AMDGPU::FEAT_PERMLANE16_SWAP)
3291 : target.has(llvm::AMDGPU::FEAT_PERMLANE32_SWAP);
3292 if (!supported)
3293 return op->emitOpError("permlane_swap of row length ")
3294 << rowLength << " is not supported on " << target.getArchName();
3295
3296 Location loc = op.getLoc();
3297 Type i32 = rewriter.getI32Type();
3298 Value src = adaptor.getSrc();
3299 bool fi = op.getFetchInactive();
3300 bool boundctrl = op.getBoundCtrl();
3301
3302 SmallVector<Value> decomposed;
3303 if (failed(LLVM::decomposeValue(rewriter, loc, src, i32, decomposed)))
3304 return rewriter.notifyMatchFailure(op,
3305 "failed to decompose value to i32");
3306
3307 SmallVector<Value> permuted;
3308 for (Value v : decomposed) {
3309 Value res;
3310 Type i32pair = LLVM::LLVMStructType::getLiteral(
3311 rewriter.getContext(), {v.getType(), v.getType()});
3312
3313 if (rowLength == 16)
3314 res = ROCDL::Permlane16SwapOp::create(rewriter, loc, i32pair, v, v, fi,
3315 boundctrl);
3316 else if (rowLength == 32)
3317 res = ROCDL::Permlane32SwapOp::create(rewriter, loc, i32pair, v, v, fi,
3318 boundctrl);
3319 else
3320 llvm_unreachable("unsupported row length");
3321
3322 Value vdst0 = LLVM::ExtractValueOp::create(rewriter, loc, res, {0});
3323 Value vdst1 = LLVM::ExtractValueOp::create(rewriter, loc, res, {1});
3324
3325 Value isEqual = LLVM::ICmpOp::create(rewriter, loc,
3326 LLVM::ICmpPredicate::eq, vdst0, v);
3327
3328 // Per `permlane(16|32)` semantics: if the first extracted element equals
3329 // 'v', the result is the second element; otherwise it is the first.
3330 Value vdstNew =
3331 LLVM::SelectOp::create(rewriter, loc, isEqual, vdst1, vdst0);
3332 permuted.emplace_back(vdstNew);
3333 }
3334
3335 Value result = LLVM::composeValue(rewriter, loc, permuted, src.getType());
3336 rewriter.replaceOp(op, result);
3337 return success();
3338 }
3339};
3340
3341struct AMDGPUPermlaneVarLowering
3342 : public ConvertOpToLLVMPattern<PermlaneVarOp> {
3344
3345 AMDGPUPermlaneVarLowering(const LLVMTypeConverter &converter,
3346 const ROCDL::TargetInfo &target)
3347 : ConvertOpToLLVMPattern<PermlaneVarOp>(converter), target(target) {}
3348 ROCDL::TargetInfo target;
3349
3350 LogicalResult
3351 matchAndRewrite(PermlaneVarOp op, OpAdaptor adaptor,
3352 ConversionPatternRewriter &rewriter) const override {
3353 if (!target.has(llvm::AMDGPU::FEAT_GFX12_INSTS))
3354 return op->emitOpError("permlane_var is only supported on GFX12+");
3355
3356 Location loc = op.getLoc();
3357 Type i32 = rewriter.getI32Type();
3358 Value src = adaptor.getSrc();
3359 Value selector = adaptor.getSelector();
3360 bool cross = op.getCross();
3361 bool fi = op.getFetchInactive();
3362 bool boundCtrl = op.getBoundCtrl();
3363
3364 SmallVector<Value> decomposed;
3365 if (failed(LLVM::decomposeValue(rewriter, loc, src, i32, decomposed)))
3366 return rewriter.notifyMatchFailure(op,
3367 "failed to decompose value to i32");
3368
3369 SmallVector<Value> permuted;
3370 for (Value v : decomposed) {
3371 Value res;
3372 if (cross)
3373 res = ROCDL::PermlaneX16VarOp::create(rewriter, loc, i32, v, v,
3374 selector, fi, boundCtrl);
3375 else
3376 res = ROCDL::Permlane16VarOp::create(rewriter, loc, i32, v, v, selector,
3377 fi, boundCtrl);
3378 permuted.emplace_back(res);
3379 }
3380
3381 Value result = LLVM::composeValue(rewriter, loc, permuted, src.getType());
3382 rewriter.replaceOp(op, result);
3383 return success();
3384 }
3385};
3386
3387//===----------------------------------------------------------------------===//
3388// In-LDS Barrier Operations
3389//===----------------------------------------------------------------------===//
3390
3391// Bit layout of ds_barrier_state (as i64):
3392// [63:32] init count (32 bits)
3393// [31:29] phase (3 bits)
3394// [28:0] pending count (29 bits)
3395constexpr int32_t kDsBarrierPendingCountBitWidth = 29;
3396constexpr int32_t kDsBarrierPhasePos = kDsBarrierPendingCountBitWidth;
3397constexpr int32_t kDsBarrierInitCountPos = 32;
3398constexpr int32_t kDsBarrierPendingCountMask =
3399 (1 << kDsBarrierPendingCountBitWidth) - 1;
3400
3401struct DsBarrierInitOpLowering
3402 : public ConvertOpToLLVMPattern<DsBarrierInitOp> {
3403 ROCDL::TargetInfo target;
3404
3405 DsBarrierInitOpLowering(const LLVMTypeConverter &converter,
3406 const ROCDL::TargetInfo &target)
3407 : ConvertOpToLLVMPattern<DsBarrierInitOp>(converter), target(target) {}
3408
3409 LogicalResult
3410 matchAndRewrite(DsBarrierInitOp op, OpAdaptor adaptor,
3411 ConversionPatternRewriter &rewriter) const override {
3412 if (!target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS))
3413 return op->emitOpError("only supported on gfx1250+");
3414
3415 Location loc = op.getLoc();
3416 Type i64 = rewriter.getI64Type();
3417
3418 MemRefType memrefType = cast<MemRefType>(op.getBase().getType());
3419 Value ptr = getStridedElementPtr(rewriter, loc, memrefType,
3420 adaptor.getBase(), adaptor.getIndices());
3421
3422 // Note: We give participants as the number of arrivals that have to occur
3423 // before the phase changes. Hardware changes the phase when updating the
3424 // pending count would underflow, so we subtract 1 to get the behavior we're
3425 // looking for.
3426 Value initCount =
3427 LLVM::SubOp::create(rewriter, loc, adaptor.getParticipants(),
3428 createI32Constant(rewriter, loc, 1));
3429
3430 // Just a bit of paranoia, but this also allows for configurable width if
3431 // that becomes a thing.
3432 Value countMask =
3433 createI32Constant(rewriter, loc, kDsBarrierPendingCountMask);
3434 Value maskedCount32 =
3435 LLVM::AndOp::create(rewriter, loc, initCount, countMask);
3436 Value maskedCount = LLVM::ZExtOp::create(rewriter, loc, i64, maskedCount32);
3437
3438 Value initCountShifted = LLVM::ShlOp::create(
3439 rewriter, loc, maskedCount,
3440 createI64Constant(rewriter, loc, kDsBarrierInitCountPos));
3441 Value barrierState =
3442 LLVM::OrOp::create(rewriter, loc, initCountShifted, maskedCount);
3443
3444 LLVM::StoreOp::create(
3445 rewriter, loc, barrierState, ptr, /*alignment=*/8, /*isVolatile=*/false,
3446 /*isNonTemporal=*/false,
3447 /*isInvariantGroup=*/false, LLVM::AtomicOrdering::release,
3448 /*syncscope=*/"workgroup");
3449
3450 rewriter.eraseOp(op);
3451 return success();
3452 }
3453};
3454
3455struct DsBarrierPollStateOpLowering
3456 : public ConvertOpToLLVMPattern<DsBarrierPollStateOp> {
3457 ROCDL::TargetInfo target;
3458
3459 DsBarrierPollStateOpLowering(const LLVMTypeConverter &converter,
3460 const ROCDL::TargetInfo &target)
3461 : ConvertOpToLLVMPattern<DsBarrierPollStateOp>(converter),
3462 target(target) {}
3463
3464 LogicalResult
3465 matchAndRewrite(DsBarrierPollStateOp op, OpAdaptor adaptor,
3466 ConversionPatternRewriter &rewriter) const override {
3467 if (!target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS))
3468 return op->emitOpError("only supported on gfx1250+");
3469
3470 Location loc = op.getLoc();
3471 Type i64 = rewriter.getI64Type();
3472
3473 MemRefType memrefType = cast<MemRefType>(op.getBase().getType());
3474 Value ptr = getStridedElementPtr(rewriter, loc, memrefType,
3475 adaptor.getBase(), adaptor.getIndices());
3476
3477 // Atomic load with workgroup scope and acquire ordering should be what
3478 // we're looking for.
3479 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(
3480 op, i64, ptr, /*alignment=*/8, /*volatile_=*/false,
3481 /*nontemporal=*/false, /*invariant=*/false,
3482 /*invariantGroup=*/false, /*noundef=*/false,
3483 LLVM::AtomicOrdering::acquire,
3484 /*syncscope=*/"workgroup");
3485 return success();
3486 }
3487};
3488
3489struct DsAsyncBarrierArriveOpLowering
3490 : public ConvertOpToLLVMPattern<DsAsyncBarrierArriveOp> {
3491 ROCDL::TargetInfo target;
3492
3493 DsAsyncBarrierArriveOpLowering(const LLVMTypeConverter &converter,
3494 const ROCDL::TargetInfo &target)
3495 : ConvertOpToLLVMPattern<DsAsyncBarrierArriveOp>(converter),
3496 target(target) {}
3497
3498 LogicalResult
3499 matchAndRewrite(DsAsyncBarrierArriveOp op, OpAdaptor adaptor,
3500 ConversionPatternRewriter &rewriter) const override {
3501 if (!target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS))
3502 return op->emitOpError("only supported on gfx1250+");
3503
3504 Location loc = op.getLoc();
3505
3506 MemRefType memrefType = cast<MemRefType>(op.getBase().getType());
3507 Value ptr = getStridedElementPtr(rewriter, loc, memrefType,
3508 adaptor.getBase(), adaptor.getIndices());
3509
3510 rewriter.replaceOpWithNewOp<ROCDL::DsAtomicAsyncBarrierArriveOp>(
3511 op, ptr, /*alias_scopes=*/nullptr, /*noalias_scopes=*/nullptr,
3512 /*tbaa=*/nullptr);
3513 return success();
3514 }
3515};
3516
3517struct DsBarrierArriveOpLowering
3518 : public ConvertOpToLLVMPattern<DsBarrierArriveOp> {
3519 ROCDL::TargetInfo target;
3520
3521 DsBarrierArriveOpLowering(const LLVMTypeConverter &converter,
3522 const ROCDL::TargetInfo &target)
3523 : ConvertOpToLLVMPattern<DsBarrierArriveOp>(converter), target(target) {}
3524
3525 LogicalResult
3526 matchAndRewrite(DsBarrierArriveOp op, OpAdaptor adaptor,
3527 ConversionPatternRewriter &rewriter) const override {
3528 if (!target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS))
3529 return op->emitOpError("only supported on gfx1250+");
3530
3531 Location loc = op.getLoc();
3532 Type i64 = rewriter.getI64Type();
3533
3534 MemRefType memrefType = cast<MemRefType>(op.getBase().getType());
3535 Value ptr = getStridedElementPtr(rewriter, loc, memrefType,
3536 adaptor.getBase(), adaptor.getIndices());
3537
3538 rewriter.replaceOpWithNewOp<ROCDL::DsAtomicBarrierArriveRtnOp>(
3539 op, i64, ptr, adaptor.getCount(), /*alias_scopes=*/nullptr,
3540 /*noalias_scopes=*/nullptr, /*tbaa=*/nullptr);
3541 return success();
3542 }
3543};
3544
3545struct DsBarrierStatePhaseOpLowering
3546 : public ConvertOpToLLVMPattern<DsBarrierStatePhaseOp> {
3548
3549 LogicalResult
3550 matchAndRewrite(DsBarrierStatePhaseOp op, OpAdaptor adaptor,
3551 ConversionPatternRewriter &rewriter) const override {
3552 Location loc = op.getLoc();
3553 Type i32 = rewriter.getI32Type();
3554
3555 Value state = adaptor.getState();
3556
3557 Value noInitCount = LLVM::TruncOp::create(rewriter, loc, i32, state);
3558 Value phase = LLVM::LShrOp::create(
3559 rewriter, loc, noInitCount,
3560 createI32Constant(rewriter, loc, kDsBarrierPhasePos));
3561
3562 rewriter.replaceOp(op, phase);
3563 return success();
3564 }
3565};
3566
3567struct DsBarrierStatePendingCountOpLowering
3568 : public ConvertOpToLLVMPattern<DsBarrierStatePendingCountOp> {
3570
3571 LogicalResult
3572 matchAndRewrite(DsBarrierStatePendingCountOp op, OpAdaptor adaptor,
3573 ConversionPatternRewriter &rewriter) const override {
3574 Location loc = op.getLoc();
3575 Type i32 = rewriter.getI32Type();
3576
3577 Value state = adaptor.getState();
3578
3579 Value noInitCount = LLVM::TruncOp::create(rewriter, loc, i32, state);
3580 Value pendingCount = LLVM::AndOp::create(
3581 rewriter, loc, noInitCount,
3582 createI32Constant(rewriter, loc,
3583 static_cast<uint32_t>(kDsBarrierPendingCountMask)));
3584
3585 rewriter.replaceOp(op, pendingCount);
3586 return success();
3587 }
3588};
3589
3590struct DsBarrierStateInitCountOpLowering
3591 : public ConvertOpToLLVMPattern<DsBarrierStateInitCountOp> {
3593
3594 LogicalResult
3595 matchAndRewrite(DsBarrierStateInitCountOp op, OpAdaptor adaptor,
3596 ConversionPatternRewriter &rewriter) const override {
3597 Location loc = op.getLoc();
3598 Type i32 = rewriter.getI32Type();
3599
3600 Value state = adaptor.getState();
3601
3602 Value initCountI64 = LLVM::LShrOp::create(
3603 rewriter, loc, state,
3604 createI64Constant(rewriter, loc, kDsBarrierInitCountPos));
3605 Value initCount = LLVM::TruncOp::create(rewriter, loc, i32, initCountI64);
3606
3607 rewriter.replaceOp(op, initCount);
3608 return success();
3609 }
3610};
3611
3612struct DsBarrierStatePhaseParityLowering
3613 : public ConvertOpToLLVMPattern<DsBarrierStatePhaseParity> {
3615
3616 LogicalResult
3617 matchAndRewrite(DsBarrierStatePhaseParity op, OpAdaptor adaptor,
3618 ConversionPatternRewriter &rewriter) const override {
3619 Location loc = op.getLoc();
3620 Type i1 = rewriter.getI1Type();
3621
3622 Value state = adaptor.getState();
3623
3624 Value noInitCount =
3625 LLVM::TruncOp::create(rewriter, loc, rewriter.getI32Type(), state);
3626 Value phase = LLVM::LShrOp::create(
3627 rewriter, loc, noInitCount,
3628 createI32Constant(rewriter, loc, kDsBarrierPhasePos));
3629 Value parity = LLVM::TruncOp::create(rewriter, loc, i1, phase);
3630
3631 rewriter.replaceOp(op, parity);
3632 return success();
3633 }
3634};
3635
3636//===----------------------------------------------------------------------===//
3637// Tensor Data Mover (TDM)
3638//===----------------------------------------------------------------------===//
3639
3640static Value setValueAtOffset(ConversionPatternRewriter &rewriter, Location loc,
3641 Value accumulator, Value value, int64_t shift) {
3642 shift = shift % 32;
3643 Value shiftAmount;
3644 if (shift != 0) {
3645 shiftAmount = createI32Constant(rewriter, loc, shift % 32);
3646 value = LLVM::ShlOp::create(rewriter, loc, value, shiftAmount);
3647 }
3648
3649 if (matchPattern(accumulator, mlir::m_Zero()))
3650 return value;
3651
3652 constexpr bool isDisjoint = true;
3653 return LLVM::OrOp::create(rewriter, loc, accumulator, value, isDisjoint);
3654}
3655
3656template <typename BaseOp>
3657struct AMDGPUMakeDmaBaseLowering : public ConvertOpToLLVMPattern<BaseOp> {
3658 using ConvertOpToLLVMPattern<BaseOp>::ConvertOpToLLVMPattern;
3659 using Adaptor = typename ConvertOpToLLVMPattern<BaseOp>::OpAdaptor;
3660
3661 AMDGPUMakeDmaBaseLowering(const LLVMTypeConverter &converter,
3662 const ROCDL::TargetInfo &target)
3663 : ConvertOpToLLVMPattern<BaseOp>(converter), target(target) {}
3664 ROCDL::TargetInfo target;
3665
3666 LogicalResult
3667 matchAndRewrite(BaseOp op, Adaptor adaptor,
3668 ConversionPatternRewriter &rewriter) const override {
3669 if (!target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS))
3670 return op->emitOpError("make_dma_base is only supported on gfx1250");
3671
3672 Location loc = op.getLoc();
3673
3674 constexpr int32_t constlen = 4;
3675 Value consts[constlen];
3676 for (int64_t i = 0; i < constlen; ++i)
3677 consts[i] = createI32Constant(rewriter, loc, i);
3678
3679 constexpr int32_t sgprslen = constlen;
3680 Value sgprs[sgprslen];
3681 for (int64_t i = 0; i < sgprslen; ++i) {
3682 sgprs[i] = consts[0];
3683 }
3684
3685 sgprs[0] = consts[1];
3686
3687 if constexpr (BaseOp::isGather()) {
3688 sgprs[0] = setValueAtOffset(rewriter, loc, sgprs[0], consts[1], 30);
3689
3690 auto type = cast<TDMGatherBaseType>(op.getResult().getType());
3691 Type indexType = type.getIndexType();
3692 unsigned indexSize = indexType.getIntOrFloatBitWidth();
3693 assert(llvm::is_contained({16u, 32u}, indexSize) &&
3694 "expected index_size to be 16 or 32");
3695 unsigned idx = (indexSize / 16) - 1;
3696
3697 if (idx)
3698 sgprs[0] = setValueAtOffset(rewriter, loc, sgprs[0], consts[1], 31);
3699 }
3700
3701 ValueRange ldsIndices = adaptor.getLdsIndices();
3702 Value lds = adaptor.getLds();
3703 auto ldsMemRefType = cast<MemRefType>(op.getLds().getType());
3704
3706 rewriter, loc, ldsMemRefType, lds, ldsIndices);
3707
3708 ValueRange globalIndices = adaptor.getGlobalIndices();
3709 Value global = adaptor.getGlobal();
3710 auto globalMemRefType = cast<MemRefType>(op.getGlobal().getType());
3711
3713 rewriter, loc, globalMemRefType, global, globalIndices);
3714
3715 Type i32 = rewriter.getI32Type();
3716 Type i64 = rewriter.getI64Type();
3717
3718 sgprs[1] = LLVM::PtrToIntOp::create(rewriter, loc, i32, ldsPtr);
3719 Value castForGlobalAddr =
3720 LLVM::PtrToIntOp::create(rewriter, loc, i64, globalPtr);
3721
3722 sgprs[2] = LLVM::TruncOp::create(rewriter, loc, i32, castForGlobalAddr);
3723
3724 Value shift = LLVM::LShrOp::create(rewriter, loc, castForGlobalAddr,
3725 createI64Constant(rewriter, loc, 32));
3726
3727 Value highHalf = LLVM::TruncOp::create(rewriter, loc, i32, shift);
3728
3729 Value mask = createI32Constant(rewriter, loc, (1ull << 25) - 1);
3730 highHalf = LLVM::AndOp::create(rewriter, loc, highHalf, mask);
3731
3732 sgprs[3] = setValueAtOffset(rewriter, loc, highHalf, consts[2], 30);
3733
3734 Type v4i32 = this->typeConverter->convertType(VectorType::get(4, i32));
3735 assert(v4i32 && "expected type conversion to succeed");
3736 Value result = LLVM::PoisonOp::create(rewriter, loc, v4i32);
3737
3738 for (auto [sgpr, constant] : llvm::zip_equal(sgprs, consts))
3739 result =
3740 LLVM::InsertElementOp::create(rewriter, loc, result, sgpr, constant);
3741
3742 rewriter.replaceOp(op, result);
3743 return success();
3744 }
3745};
3746
3747template <typename DescriptorOp>
3748struct AMDGPULowerDescriptor : public ConvertOpToLLVMPattern<DescriptorOp> {
3749 using ConvertOpToLLVMPattern<DescriptorOp>::ConvertOpToLLVMPattern;
3750 using OpAdaptor = typename ConvertOpToLLVMPattern<DescriptorOp>::OpAdaptor;
3751
3752 AMDGPULowerDescriptor(const LLVMTypeConverter &converter,
3753 const ROCDL::TargetInfo &target)
3754 : ConvertOpToLLVMPattern<DescriptorOp>(converter), target(target) {}
3755 ROCDL::TargetInfo target;
3756
3757 Value getDGroup0(OpAdaptor &adaptor) const { return adaptor.getBase(); }
3758
3759 Value setWorkgroupMask(DescriptorOp op, OpAdaptor &adaptor,
3760 ConversionPatternRewriter &rewriter, Location loc,
3761 Value sgpr0) const {
3762 Value mask = op.getWorkgroupMask();
3763 if (!mask)
3764 return sgpr0;
3765
3766 Type i16 = rewriter.getI16Type();
3767 mask = LLVM::BitcastOp::create(rewriter, loc, i16, mask);
3768 Type i32 = rewriter.getI32Type();
3769 Value extendedMask = LLVM::ZExtOp::create(rewriter, loc, i32, mask);
3770 return setValueAtOffset(rewriter, loc, sgpr0, extendedMask, 0);
3771 }
3772
3773 Value setDataSize(DescriptorOp op, OpAdaptor &adaptor,
3774 ConversionPatternRewriter &rewriter, Location loc,
3775 Value sgpr0, ArrayRef<Value> consts) const {
3776 unsigned elementTypeWidthInBits = op.getElementTypeWidth();
3777 assert(llvm::is_contained({8u, 16u, 32u, 64u}, elementTypeWidthInBits) &&
3778 "expected type width to be 8, 16, 32, or 64.");
3779 int64_t idx = llvm::Log2_32(elementTypeWidthInBits / 8);
3780 Value size = consts[idx];
3781 return setValueAtOffset(rewriter, loc, sgpr0, size, 16);
3782 }
3783
3784 Value setAtomicBarrier(DescriptorOp op, OpAdaptor &adaptor,
3785 ConversionPatternRewriter &rewriter, Location loc,
3786 Value sgpr0, ArrayRef<Value> consts) const {
3787 if (!adaptor.getAtomicBarrierAddress())
3788 return sgpr0;
3789
3790 return setValueAtOffset(rewriter, loc, sgpr0, consts[1], 18);
3791 }
3792
3793 Value setIterateEnable(DescriptorOp op, OpAdaptor &adaptor,
3794 ConversionPatternRewriter &rewriter, Location loc,
3795 Value sgpr0, ArrayRef<Value> consts) const {
3796 if (!adaptor.getGlobalIncrement())
3797 return sgpr0;
3798
3799 // Value is ignored when in gather mode.
3800 // TODO: emit error earlier?
3801 return setValueAtOffset(rewriter, loc, sgpr0, consts[1], 19);
3802 }
3803
3804 Value setPadEnable(DescriptorOp op, OpAdaptor &adaptor,
3805 ConversionPatternRewriter &rewriter, Location loc,
3806 Value sgpr0, ArrayRef<Value> consts) const {
3807 if (!op.getPadAmount())
3808 return sgpr0;
3809
3810 return setValueAtOffset(rewriter, loc, sgpr0, consts[1], 20);
3811 }
3812
3813 Value setEarlyTimeout(DescriptorOp op, OpAdaptor &adaptor,
3814 ConversionPatternRewriter &rewriter, Location loc,
3815 Value sgpr0, ArrayRef<Value> consts) const {
3816 if (!op.getWorkgroupMask())
3817 return sgpr0;
3818
3819 return setValueAtOffset(rewriter, loc, sgpr0, consts[1], 21);
3820 }
3821
3822 Value setPadInterval(DescriptorOp op, OpAdaptor &adaptor,
3823 ConversionPatternRewriter &rewriter, Location loc,
3824 Value sgpr0, ArrayRef<Value> consts) const {
3825 if (!op.getPadAmount())
3826 return sgpr0;
3827
3828 // pre-condition: padInterval can be a power of two between 2 and 256.
3829 // TODO: Validation if the value breaks the pre-condition.
3830 // If the pre-condition fails, there is a possibility of
3831 // affecting the higher bits. In a following PR implement
3832 // RuntimeVerifiableOpInterface that instruments conditions that need to be
3833 // checked at runtime.
3834 IntegerType i32 = rewriter.getI32Type();
3835 Value padInterval = adaptor.getPadInterval();
3836 padInterval = LLVM::CountTrailingZerosOp::create(rewriter, loc, i32,
3837 padInterval, false);
3838 padInterval = LLVM::SubOp::create(rewriter, loc, padInterval, consts[1]);
3839 // post-condition: padInterval can be a value between 0 and 7.
3840 return setValueAtOffset(rewriter, loc, sgpr0, padInterval, 22);
3841 }
3842
3843 Value setPadAmount(DescriptorOp op, OpAdaptor &adaptor,
3844 ConversionPatternRewriter &rewriter, Location loc,
3845 Value sgpr0, ArrayRef<Value> consts) const {
3846 if (!op.getPadAmount())
3847 return sgpr0;
3848
3849 // pre-condition: padAmount is a value between 1-128.
3850 // TODO: Validation if the value breaks the pre-condition.
3851 // If the pre-condition fails, there is a possibility of
3852 // affecting the higher bits. In a following PR implement
3853 // RuntimeVerifiableOpInterface that instruments conditions that need to be
3854 // checked at runtime.
3855 Value padAmount = adaptor.getPadAmount();
3856 padAmount = LLVM::SubOp::create(rewriter, loc, padAmount, consts[1]);
3857 // post-condition: padAmount is a value between 0-127.
3858 return setValueAtOffset(rewriter, loc, sgpr0, padAmount, 25);
3859 }
3860
3861 Value setAtomicBarrierAddress(DescriptorOp op, OpAdaptor &adaptor,
3862 ConversionPatternRewriter &rewriter,
3863 Location loc, Value sgpr1,
3864 ArrayRef<Value> consts) const {
3865 if (!adaptor.getAtomicBarrierAddress())
3866 return sgpr1;
3867
3868 Value atomicBarrierAddress = adaptor.getAtomicBarrierAddress();
3869 auto barrierAddressTy =
3870 cast<MemRefType>(op.getAtomicBarrierAddress().getType());
3871 ValueRange atomicBarrierIndices = adaptor.getAtomicBarrierIndices();
3872 atomicBarrierAddress = ConvertToLLVMPattern::getStridedElementPtr(
3873 rewriter, loc, barrierAddressTy, atomicBarrierAddress,
3874 atomicBarrierIndices);
3875 IntegerType i32 = rewriter.getI32Type();
3876 // pre-condition: atomicBarrierAddress is aligned to 8 bytes which implies
3877 // that the 3 LSBs are zero.
3878 // TODO: Validation if the value breaks the pre-condition.
3879 // In a following PR implement RuntimeVerifiableOpInterface
3880 // that instruments conditions that need to be checked at runtime.
3881 atomicBarrierAddress =
3882 LLVM::PtrToIntOp::create(rewriter, loc, i32, atomicBarrierAddress);
3883 atomicBarrierAddress =
3884 LLVM::LShrOp::create(rewriter, loc, atomicBarrierAddress, consts[3]);
3885 Value mask = createI32Constant(rewriter, loc, 0xFFFF);
3886 atomicBarrierAddress =
3887 LLVM::AndOp::create(rewriter, loc, atomicBarrierAddress, mask);
3888 return setValueAtOffset(rewriter, loc, sgpr1, atomicBarrierAddress, 32);
3889 }
3890
3891 std::pair<Value, Value> setTensorDimX(DescriptorOp op, OpAdaptor &adaptor,
3892 ConversionPatternRewriter &rewriter,
3893 Location loc, Value sgpr1, Value sgpr2,
3894 ArrayRef<Value> consts, uint64_t dimX,
3895 uint32_t offset) const {
3896 ArrayRef<int64_t> globalStaticSizes = adaptor.getGlobalStaticSizes();
3897 ValueRange globalDynamicSizes = adaptor.getGlobalDynamicSizes();
3898 SmallVector<OpFoldResult> mixedGlobalSizes =
3899 getMixedValues(globalStaticSizes, globalDynamicSizes, rewriter);
3900 if (mixedGlobalSizes.size() <= dimX)
3901 return {sgpr1, sgpr2};
3902
3903 OpFoldResult tensorDimXOpFoldResult = *(mixedGlobalSizes.rbegin() + dimX);
3904 // pre-condition: tensorDimX is less than 2^32-1
3905 // TODO: Validation if the value breaks the pre-condition.
3906 // In a following PR implement RuntimeVerifiableOpInterface that instruments
3907 // conditions that need to be checked at runtime. This could also be fixed
3908 // by saying that mixedGlobalSizes is a DynamicI32List.
3909 Value tensorDimX;
3910 if (auto attr = dyn_cast<Attribute>(tensorDimXOpFoldResult)) {
3911 tensorDimX =
3912 createI32Constant(rewriter, loc, cast<IntegerAttr>(attr).getInt());
3913 } else {
3914 IntegerType i32 = rewriter.getI32Type();
3915 tensorDimX = cast<Value>(tensorDimXOpFoldResult);
3916 tensorDimX = LLVM::TruncOp::create(rewriter, loc, i32, tensorDimX);
3917 }
3918
3919 sgpr1 = setValueAtOffset(rewriter, loc, sgpr1, tensorDimX, offset);
3920
3921 Value c16 = createI32Constant(rewriter, loc, 16);
3922 Value tensorDimXHigh = LLVM::LShrOp::create(rewriter, loc, tensorDimX, c16);
3923 sgpr2 = setValueAtOffset(rewriter, loc, sgpr2, tensorDimXHigh, offset + 16);
3924 return {sgpr1, sgpr2};
3925 }
3926
3927 std::pair<Value, Value> setTensorDim0(DescriptorOp op, OpAdaptor &adaptor,
3928 ConversionPatternRewriter &rewriter,
3929 Location loc, Value sgpr1, Value sgpr2,
3930 ArrayRef<Value> consts) const {
3931 return setTensorDimX(op, adaptor, rewriter, loc, sgpr1, sgpr2, consts, 0,
3932 48);
3933 }
3934
3935 std::pair<Value, Value> setTensorDim1(DescriptorOp op, OpAdaptor &adaptor,
3936 ConversionPatternRewriter &rewriter,
3937 Location loc, Value sgpr2, Value sgpr3,
3938 ArrayRef<Value> consts) const {
3939 return setTensorDimX(op, adaptor, rewriter, loc, sgpr2, sgpr3, consts, 1,
3940 80);
3941 }
3942
3943 Value setTileDimX(DescriptorOp op, OpAdaptor &adaptor,
3944 ConversionPatternRewriter &rewriter, Location loc,
3945 Value sgpr, ArrayRef<Value> consts, size_t dimX,
3946 int64_t offset) const {
3947 ArrayRef<int64_t> sharedStaticSizes = adaptor.getSharedStaticSizes();
3948 ValueRange sharedDynamicSizes = adaptor.getSharedDynamicSizes();
3949 SmallVector<OpFoldResult> mixedSharedSizes =
3950 getMixedValues(sharedStaticSizes, sharedDynamicSizes, rewriter);
3951 if (mixedSharedSizes.size() <= dimX)
3952 return sgpr;
3953
3954 OpFoldResult tileDimXOpFoldResult = *(mixedSharedSizes.rbegin() + dimX);
3955 // pre-condition: tileDimX is less than 2^16-1
3956 // TODO: Validation if the value breaks the pre-condition.
3957 // If the pre-condition fails, there is a possibility of
3958 // affecting the higher bits. In a following PR implement
3959 // RuntimeVerifiableOpInterface that instruments conditions that need to be
3960 // checked at runtime. This could also be fixed by saying that
3961 // mixedSharedSizes is a DynamicI16List.
3962 Value tileDimX;
3963 if (auto attr = dyn_cast<Attribute>(tileDimXOpFoldResult)) {
3964 tileDimX =
3965 createI32Constant(rewriter, loc, cast<IntegerAttr>(attr).getInt());
3966 } else {
3967 IntegerType i32 = rewriter.getI32Type();
3968 tileDimX = cast<Value>(tileDimXOpFoldResult);
3969 tileDimX = LLVM::TruncOp::create(rewriter, loc, i32, tileDimX);
3970 }
3971
3972 return setValueAtOffset(rewriter, loc, sgpr, tileDimX, offset);
3973 }
3974
3975 Value setTileDim0(DescriptorOp op, OpAdaptor &adaptor,
3976 ConversionPatternRewriter &rewriter, Location loc,
3977 Value sgpr3, ArrayRef<Value> consts) const {
3978 return setTileDimX(op, adaptor, rewriter, loc, sgpr3, consts, 0, 112);
3979 }
3980
3981 Value setTileDim1(DescriptorOp op, OpAdaptor &adaptor,
3982 ConversionPatternRewriter &rewriter, Location loc,
3983 Value sgpr4, ArrayRef<Value> consts) const {
3984 return setTileDimX(op, adaptor, rewriter, loc, sgpr4, consts, 1, 128);
3985 }
3986
3987 Value setValidIndices(DescriptorOp op, OpAdaptor &adaptor,
3988 ConversionPatternRewriter &rewriter, Location loc,
3989 Value sgpr4, ArrayRef<Value> consts) const {
3990 auto type = cast<VectorType>(op.getIndices().getType());
3991 ArrayRef<int64_t> shape = type.getShape();
3992 assert(shape.size() == 1 && "expected shape to be of rank 1.");
3993 unsigned length = shape.back();
3994 assert(0 < length && length <= 16 && "expected length to be at most 16.");
3995 Value value = createI32Constant(rewriter, loc, length);
3996 return setValueAtOffset(rewriter, loc, sgpr4, value, 128);
3997 }
3998
3999 Value setTileDim1OrValidIndices(DescriptorOp op, OpAdaptor &adaptor,
4000 ConversionPatternRewriter &rewriter,
4001 Location loc, Value sgpr4,
4002 ArrayRef<Value> consts) const {
4003 if constexpr (DescriptorOp::isGather())
4004 return setValidIndices(op, adaptor, rewriter, loc, sgpr4, consts);
4005 return setTileDim1(op, adaptor, rewriter, loc, sgpr4, consts);
4006 }
4007
4008 Value setTileDim2(DescriptorOp op, OpAdaptor &adaptor,
4009 ConversionPatternRewriter &rewriter, Location loc,
4010 Value sgpr4, ArrayRef<Value> consts) const {
4011 // Value is ignored when in gather mode.
4012 if constexpr (DescriptorOp::isGather())
4013 return sgpr4;
4014 return setTileDimX(op, adaptor, rewriter, loc, sgpr4, consts, 2, 144);
4015 }
4016
4017 std::pair<Value, Value>
4018 setTensorDimXStride(DescriptorOp op, OpAdaptor &adaptor,
4019 ConversionPatternRewriter &rewriter, Location loc,
4020 Value sgprY, Value sgprZ, ArrayRef<Value> consts,
4021 size_t dimX, int64_t offset) const {
4022 ArrayRef<int64_t> globalStaticStrides = adaptor.getGlobalStaticStrides();
4023 ValueRange globalDynamicStrides = adaptor.getGlobalDynamicStrides();
4024 SmallVector<OpFoldResult> mixedGlobalStrides =
4025 getMixedValues(globalStaticStrides, globalDynamicStrides, rewriter);
4026
4027 if (mixedGlobalStrides.size() <= (dimX + 1))
4028 return {sgprY, sgprZ};
4029
4030 OpFoldResult tensorDimXStrideOpFoldResult =
4031 *(mixedGlobalStrides.rbegin() + dimX + 1);
4032 // pre-condition: tensorDimXStride is less than 2^48-1
4033 // TODO: Validation if the value breaks the pre-condition.
4034 // In a following PR implement RuntimeVerifiableOpInterface that instruments
4035 // conditions that need to be checked at runtime.
4036 Value tensorDimXStride;
4037 if (auto attr = dyn_cast<Attribute>(tensorDimXStrideOpFoldResult))
4038 tensorDimXStride =
4039 createI64Constant(rewriter, loc, cast<IntegerAttr>(attr).getInt());
4040 else
4041 tensorDimXStride = cast<Value>(tensorDimXStrideOpFoldResult);
4042
4043 constexpr int64_t first48bits = (1ll << 48) - 1;
4044 Value mask = createI64Constant(rewriter, loc, first48bits);
4045 tensorDimXStride =
4046 LLVM::AndOp::create(rewriter, loc, mask, tensorDimXStride);
4047 IntegerType i32 = rewriter.getI32Type();
4048 Value tensorDimXStrideLow =
4049 LLVM::TruncOp::create(rewriter, loc, i32, tensorDimXStride);
4050 sgprY = setValueAtOffset(rewriter, loc, sgprY, tensorDimXStrideLow, offset);
4051
4052 int64_t shift = (offset % 32) == 0 ? 32 : offset % 32;
4053 Value shiftVal = createI64Constant(rewriter, loc, shift);
4054 Value tensorDimXStrideHigh =
4055 LLVM::LShrOp::create(rewriter, loc, tensorDimXStride, shiftVal);
4056 tensorDimXStrideHigh =
4057 LLVM::TruncOp::create(rewriter, loc, i32, tensorDimXStrideHigh);
4058 sgprZ = setValueAtOffset(rewriter, loc, sgprZ, tensorDimXStrideHigh,
4059 offset + shift);
4060 return {sgprY, sgprZ};
4061 }
4062
4063 std::pair<Value, Value>
4064 setTensorDim0Stride(DescriptorOp op, OpAdaptor &adaptor,
4065 ConversionPatternRewriter &rewriter, Location loc,
4066 Value sgpr5, Value sgpr6, ArrayRef<Value> consts) const {
4067 return setTensorDimXStride(op, adaptor, rewriter, loc, sgpr5, sgpr6, consts,
4068 0, 160);
4069 }
4070
4071 std::pair<Value, Value>
4072 setTensorDim1Stride(DescriptorOp op, OpAdaptor &adaptor,
4073 ConversionPatternRewriter &rewriter, Location loc,
4074 Value sgpr5, Value sgpr6, ArrayRef<Value> consts) const {
4075 // Value is ignored when in gather mode.
4076 if constexpr (DescriptorOp::isGather())
4077 return {sgpr5, sgpr6};
4078 return setTensorDimXStride(op, adaptor, rewriter, loc, sgpr5, sgpr6, consts,
4079 1, 208);
4080 }
4081
4082 Value getDGroup1(DescriptorOp op, OpAdaptor &adaptor,
4083 ConversionPatternRewriter &rewriter, Location loc,
4084 ArrayRef<Value> consts) const {
4085 Value sgprs[8];
4086 for (int64_t i = 0; i < 8; ++i) {
4087 sgprs[i] = consts[0];
4088 }
4089
4090 sgprs[0] = setWorkgroupMask(op, adaptor, rewriter, loc, sgprs[0]);
4091 sgprs[0] = setDataSize(op, adaptor, rewriter, loc, sgprs[0], consts);
4092 sgprs[0] = setAtomicBarrier(op, adaptor, rewriter, loc, sgprs[0], consts);
4093 sgprs[0] = setIterateEnable(op, adaptor, rewriter, loc, sgprs[0], consts);
4094 sgprs[0] = setPadEnable(op, adaptor, rewriter, loc, sgprs[0], consts);
4095 sgprs[0] = setEarlyTimeout(op, adaptor, rewriter, loc, sgprs[0], consts);
4096 sgprs[0] = setPadInterval(op, adaptor, rewriter, loc, sgprs[0], consts);
4097 sgprs[0] = setPadAmount(op, adaptor, rewriter, loc, sgprs[0], consts);
4098
4099 sgprs[1] =
4100 setAtomicBarrierAddress(op, adaptor, rewriter, loc, sgprs[1], consts);
4101 std::tie(sgprs[1], sgprs[2]) =
4102 setTensorDim0(op, adaptor, rewriter, loc, sgprs[1], sgprs[2], consts);
4103 std::tie(sgprs[2], sgprs[3]) =
4104 setTensorDim1(op, adaptor, rewriter, loc, sgprs[2], sgprs[3], consts);
4105
4106 sgprs[3] = setTileDim0(op, adaptor, rewriter, loc, sgprs[3], consts);
4107 sgprs[4] =
4108 setTileDim1OrValidIndices(op, adaptor, rewriter, loc, sgprs[4], consts);
4109 sgprs[4] = setTileDim2(op, adaptor, rewriter, loc, sgprs[4], consts);
4110 std::tie(sgprs[5], sgprs[6]) = setTensorDim0Stride(
4111 op, adaptor, rewriter, loc, sgprs[5], sgprs[6], consts);
4112 std::tie(sgprs[6], sgprs[7]) = setTensorDim1Stride(
4113 op, adaptor, rewriter, loc, sgprs[6], sgprs[7], consts);
4114
4115 IntegerType i32 = rewriter.getI32Type();
4116 Type v8i32 = this->typeConverter->convertType(VectorType::get(8, i32));
4117 assert(v8i32 && "expected type conversion to succeed");
4118 Value dgroup1 = LLVM::PoisonOp::create(rewriter, loc, v8i32);
4119
4120 for (auto [sgpr, constant] : llvm::zip_equal(sgprs, consts)) {
4121 dgroup1 =
4122 LLVM::InsertElementOp::create(rewriter, loc, dgroup1, sgpr, constant);
4123 }
4124
4125 return dgroup1;
4126 }
4127
4128 Value setTensorDimX(DescriptorOp op, OpAdaptor &adaptor,
4129 ConversionPatternRewriter &rewriter, Location loc,
4130 Value sgpr0, ArrayRef<Value> consts, int64_t dimX,
4131 int64_t offset) const {
4132 ArrayRef<int64_t> globalStaticSizes = adaptor.getGlobalStaticSizes();
4133 ValueRange globalDynamicSizes = adaptor.getGlobalDynamicSizes();
4134 SmallVector<OpFoldResult> mixedGlobalSizes =
4135 getMixedValues(globalStaticSizes, globalDynamicSizes, rewriter);
4136 if (mixedGlobalSizes.size() <= static_cast<unsigned long>(dimX))
4137 return sgpr0;
4138
4139 OpFoldResult tensorDimXOpFoldResult = *(mixedGlobalSizes.rbegin() + dimX);
4140 Value tensorDimX;
4141 if (auto attr = dyn_cast<Attribute>(tensorDimXOpFoldResult)) {
4142 tensorDimX =
4143 createI32Constant(rewriter, loc, cast<IntegerAttr>(attr).getInt());
4144 } else {
4145 IntegerType i32 = rewriter.getI32Type();
4146 tensorDimX = cast<Value>(tensorDimXOpFoldResult);
4147 tensorDimX = LLVM::TruncOp::create(rewriter, loc, i32, tensorDimX);
4148 }
4149
4150 return setValueAtOffset(rewriter, loc, sgpr0, tensorDimX, offset);
4151 }
4152
4153 Value setTensorDim2(DescriptorOp op, OpAdaptor &adaptor,
4154 ConversionPatternRewriter &rewriter, Location loc,
4155 Value sgpr0, ArrayRef<Value> consts) const {
4156 return setTensorDimX(op, adaptor, rewriter, loc, sgpr0, consts, 2, 0);
4157 }
4158
4159 Value truncateAndSetValueAtOffset(ConversionPatternRewriter &rewriter,
4160 Location loc, Value accumulator,
4161 Value value, int64_t shift) const {
4162
4163 IntegerType i32 = rewriter.getI32Type();
4164 value = LLVM::TruncOp::create(rewriter, loc, i32, value);
4165 return setValueAtOffset(rewriter, loc, accumulator, value, shift);
4166 }
4167
4168 Value setLDSAddrIncrement(DescriptorOp op, OpAdaptor &adaptor,
4169 ConversionPatternRewriter &rewriter, Location loc,
4170 Value sgpr1, ArrayRef<Value> consts,
4171 int64_t offset) const {
4172 Value ldsAddrIncrement = adaptor.getLdsIncrement();
4173 return setValueAtOffset(rewriter, loc, sgpr1, ldsAddrIncrement, offset);
4174 }
4175
4176 std::pair<Value, Value>
4177 setGlobalAddrIncrement(DescriptorOp op, OpAdaptor &adaptor,
4178 ConversionPatternRewriter &rewriter, Location loc,
4179 Value sgpr2, Value sgpr3, ArrayRef<Value> consts,
4180 int64_t offset) const {
4181 Value globalAddrIncrement = adaptor.getGlobalIncrement();
4182 sgpr2 = truncateAndSetValueAtOffset(rewriter, loc, sgpr2,
4183 globalAddrIncrement, offset);
4184 Value shift = createI64Constant(rewriter, loc, 32);
4185 globalAddrIncrement =
4186 LLVM::LShrOp::create(rewriter, loc, globalAddrIncrement, shift);
4187 constexpr int64_t first16BitsHigh = (1ll << 16) - 1;
4188 sgpr3 = truncateAndSetValueAtOffset(rewriter, loc, sgpr3,
4189 globalAddrIncrement, offset + 32);
4190 Value mask = createI32Constant(rewriter, loc, first16BitsHigh);
4191 sgpr3 = LLVM::AndOp::create(rewriter, loc, sgpr3, mask);
4192 return {sgpr2, sgpr3};
4193 }
4194
4195 Value setTensorDim3OrLDSAddrIncrement(DescriptorOp op, OpAdaptor &adaptor,
4196 ConversionPatternRewriter &rewriter,
4197 Location loc, Value sgpr1,
4198 ArrayRef<Value> consts) const {
4199 Value ldsIncrement = op.getLdsIncrement();
4200 constexpr int64_t dim = 3;
4201 constexpr int64_t offset = 32;
4202 if (!ldsIncrement)
4203 return setTensorDimX(op, adaptor, rewriter, loc, sgpr1, consts, dim,
4204 offset);
4205 return setLDSAddrIncrement(op, adaptor, rewriter, loc, sgpr1, consts,
4206 offset);
4207 }
4208
4209 std::pair<Value, Value> setTensorDim2StrideOrGlobalAddrIncrement(
4210 DescriptorOp op, OpAdaptor &adaptor, ConversionPatternRewriter &rewriter,
4211 Location loc, Value sgpr2, Value sgpr3, ArrayRef<Value> consts) const {
4212 Value globalIncrement = op.getGlobalIncrement();
4213 constexpr int32_t dim = 2;
4214 constexpr int32_t offset = 64;
4215 if (!globalIncrement)
4216 return setTensorDimXStride(op, adaptor, rewriter, loc, sgpr2, sgpr3,
4217 consts, dim, offset);
4218 return setGlobalAddrIncrement(op, adaptor, rewriter, loc, sgpr2, sgpr3,
4219 consts, offset);
4220 }
4221
4222 Value setIterateCount(DescriptorOp op, OpAdaptor &adaptor,
4223 ConversionPatternRewriter &rewriter, Location loc,
4224 Value sgpr3, ArrayRef<Value> consts,
4225 int32_t offset) const {
4226 Value iterationCount = adaptor.getIterationCount();
4227 IntegerType i32 = rewriter.getI32Type();
4228 // pre-condition: iterationCount is in the inclusive interval [1, 256].
4229 // TODO: validation if the value breaks the pre-condition.
4230 // If the pre-condition fails, there is a possibility of
4231 // affecting the higher bits. In a following PR implement
4232 // RuntimeVerifiableOpInterface that instruments conditions that need to be
4233 // checked at runtime.
4234 iterationCount = LLVM::TruncOp::create(rewriter, loc, i32, iterationCount);
4235 iterationCount =
4236 LLVM::SubOp::create(rewriter, loc, iterationCount, consts[1]);
4237 return setValueAtOffset(rewriter, loc, sgpr3, iterationCount, offset);
4238 }
4239
4240 Value setTileDim3OrIterateCount(DescriptorOp op, OpAdaptor &adaptor,
4241 ConversionPatternRewriter &rewriter,
4242 Location loc, Value sgpr3,
4243 ArrayRef<Value> consts) const {
4244 Value iterateCount = op.getIterationCount();
4245 constexpr int32_t dim = 2;
4246 constexpr int32_t offset = 112;
4247 if (!iterateCount)
4248 return setTileDimX(op, adaptor, rewriter, loc, sgpr3, consts, dim,
4249 offset);
4250
4251 return setIterateCount(op, adaptor, rewriter, loc, sgpr3, consts, offset);
4252 }
4253
4254 Value getDGroup2(DescriptorOp op, OpAdaptor &adaptor,
4255 ConversionPatternRewriter &rewriter, Location loc,
4256 ArrayRef<Value> consts) const {
4257 if constexpr (DescriptorOp::isGather())
4258 return getDGroup2Gather(op, adaptor, rewriter, loc, consts);
4259 return getDGroup2NonGather(op, adaptor, rewriter, loc, consts);
4260 }
4261
4262 Value getDGroup2NonGather(DescriptorOp op, OpAdaptor &adaptor,
4263 ConversionPatternRewriter &rewriter, Location loc,
4264 ArrayRef<Value> consts) const {
4265 IntegerType i32 = rewriter.getI32Type();
4266 Type v4i32 = this->typeConverter->convertType(VectorType::get(4, i32));
4267 assert(v4i32 && "expected type conversion to succeed.");
4268
4269 bool onlyNeedsTwoDescriptors = !op.getLdsIncrement() && op.getRank() <= 2;
4270 if (onlyNeedsTwoDescriptors)
4271 return LLVM::ZeroOp::create(rewriter, loc, v4i32);
4272
4273 constexpr int64_t sgprlen = 4;
4274 Value sgprs[sgprlen];
4275 for (int i = 0; i < sgprlen; ++i)
4276 sgprs[i] = consts[0];
4277
4278 sgprs[0] = setTensorDim2(op, adaptor, rewriter, loc, sgprs[0], consts);
4279 sgprs[1] = setTensorDim3OrLDSAddrIncrement(op, adaptor, rewriter, loc,
4280 sgprs[1], consts);
4281 std::tie(sgprs[2], sgprs[3]) = setTensorDim2StrideOrGlobalAddrIncrement(
4282 op, adaptor, rewriter, loc, sgprs[2], sgprs[3], consts);
4283 sgprs[3] =
4284 setTileDim3OrIterateCount(op, adaptor, rewriter, loc, sgprs[3], consts);
4285
4286 Value dgroup2 = LLVM::PoisonOp::create(rewriter, loc, v4i32);
4287 for (auto [sgpr, constant] : llvm::zip(sgprs, consts))
4288 dgroup2 =
4289 LLVM::InsertElementOp::create(rewriter, loc, dgroup2, sgpr, constant);
4290
4291 return dgroup2;
4292 }
4293
4294 Value getGatherIndices(DescriptorOp op, OpAdaptor &adaptor,
4295 ConversionPatternRewriter &rewriter, Location loc,
4296 ArrayRef<Value> consts, bool firstHalf) const {
4297 IntegerType i32 = rewriter.getI32Type();
4298 Type v4i32 = this->typeConverter->convertType(VectorType::get(4, i32));
4299 assert(v4i32 && "expected type conversion to succeed.");
4300
4301 Value indices = adaptor.getIndices();
4302 auto vectorType = cast<VectorType>(indices.getType());
4303 unsigned length = vectorType.getShape().back();
4304 Type elementType = vectorType.getElementType();
4305 unsigned maxLength = elementType == i32 ? 4 : 8;
4306 int32_t offset = firstHalf ? 0 : maxLength;
4307 unsigned discountedLength =
4308 std::max(static_cast<int32_t>(length - offset), 0);
4309
4310 unsigned targetSize = std::min(maxLength, discountedLength);
4311
4312 SmallVector<Value> indicesVector;
4313 for (unsigned i = offset; i < targetSize + offset; ++i) {
4314 Value idx;
4315 if (i < consts.size())
4316 idx = consts[i];
4317 else
4318 idx = createI32Constant(rewriter, loc, i);
4319 Value elem = LLVM::ExtractElementOp::create(rewriter, loc, indices, idx);
4320 indicesVector.push_back(elem);
4321 }
4322
4323 SmallVector<Value> indicesI32Vector;
4324 if (elementType == i32) {
4325 indicesI32Vector = std::move(indicesVector);
4326 } else {
4327 for (unsigned i = 0; i < targetSize; ++i) {
4328 Value index = indicesVector[i];
4329 indicesI32Vector.push_back(
4330 LLVM::ZExtOp::create(rewriter, loc, i32, index));
4331 }
4332 if ((targetSize % 2) != 0)
4333 // Add padding when not divisible by two.
4334 indicesI32Vector.push_back(consts[0]);
4335 }
4336
4337 SmallVector<Value> indicesToInsert;
4338 if (elementType == i32) {
4339 indicesToInsert = std::move(indicesI32Vector);
4340 } else {
4341 unsigned size = indicesI32Vector.size() / 2;
4342 for (unsigned i = 0; i < size; ++i) {
4343 Value first = indicesI32Vector[2 * i];
4344 Value second = indicesI32Vector[2 * i + 1];
4345 Value joined = setValueAtOffset(rewriter, loc, first, second, 16);
4346 indicesToInsert.push_back(joined);
4347 }
4348 }
4349
4350 Value dgroup = LLVM::PoisonOp::create(rewriter, loc, v4i32);
4351 for (auto [sgpr, constant] : llvm::zip_first(indicesToInsert, consts))
4352 dgroup =
4353 LLVM::InsertElementOp::create(rewriter, loc, dgroup, sgpr, constant);
4354
4355 return dgroup;
4356 }
4357
4358 Value getDGroup2Gather(DescriptorOp op, OpAdaptor &adaptor,
4359 ConversionPatternRewriter &rewriter, Location loc,
4360 ArrayRef<Value> consts) const {
4361 return getGatherIndices(op, adaptor, rewriter, loc, consts, true);
4362 }
4363
4364 std::pair<Value, Value>
4365 setTensorDim3Stride(DescriptorOp op, OpAdaptor &adaptor,
4366 ConversionPatternRewriter &rewriter, Location loc,
4367 Value sgpr0, Value sgpr1, ArrayRef<Value> consts) const {
4368 constexpr int32_t dim = 3;
4369 constexpr int32_t offset = 0;
4370 return setTensorDimXStride(op, adaptor, rewriter, loc, sgpr0, sgpr1, consts,
4371 dim, offset);
4372 }
4373
4374 std::pair<Value, Value> setTensorDim4(DescriptorOp op, OpAdaptor &adaptor,
4375 ConversionPatternRewriter &rewriter,
4376 Location loc, Value sgpr1, Value sgpr2,
4377 ArrayRef<Value> consts) const {
4378 constexpr int32_t dim = 4;
4379 constexpr int32_t offset = 48;
4380 return setTensorDimX(op, adaptor, rewriter, loc, sgpr1, sgpr2, consts, dim,
4381 offset);
4382 }
4383
4384 Value setTileDim4(DescriptorOp op, OpAdaptor &adaptor,
4385 ConversionPatternRewriter &rewriter, Location loc,
4386 Value sgpr2, ArrayRef<Value> consts) const {
4387 constexpr int32_t dim = 4;
4388 constexpr int32_t offset = 80;
4389 return setTileDimX(op, adaptor, rewriter, loc, sgpr2, consts, dim, offset);
4390 }
4391
4392 Value getDGroup3(DescriptorOp op, OpAdaptor &adaptor,
4393 ConversionPatternRewriter &rewriter, Location loc,
4394 ArrayRef<Value> consts) const {
4395 if constexpr (DescriptorOp::isGather())
4396 return getDGroup3Gather(op, adaptor, rewriter, loc, consts);
4397 return getDGroup3NonGather(op, adaptor, rewriter, loc, consts);
4398 }
4399
4400 Value getDGroup3NonGather(DescriptorOp op, OpAdaptor &adaptor,
4401 ConversionPatternRewriter &rewriter, Location loc,
4402 ArrayRef<Value> consts) const {
4403 IntegerType i32 = rewriter.getI32Type();
4404 Type v4i32 = this->typeConverter->convertType(VectorType::get(4, i32));
4405 assert(v4i32 && "expected type conversion to succeed.");
4406 bool onlyNeedsTwoDescriptors = !op.getLdsIncrement() && op.getRank() <= 2;
4407 if (onlyNeedsTwoDescriptors)
4408 return LLVM::ZeroOp::create(rewriter, loc, v4i32);
4409
4410 constexpr int32_t sgprlen = 4;
4411 Value sgprs[sgprlen];
4412 for (int i = 0; i < sgprlen; ++i)
4413 sgprs[i] = consts[0];
4414
4415 std::tie(sgprs[0], sgprs[1]) = setTensorDim3Stride(
4416 op, adaptor, rewriter, loc, sgprs[0], sgprs[1], consts);
4417 std::tie(sgprs[1], sgprs[2]) =
4418 setTensorDim4(op, adaptor, rewriter, loc, sgprs[1], sgprs[2], consts);
4419 sgprs[2] = setTileDim4(op, adaptor, rewriter, loc, sgprs[2], consts);
4420
4421 Value dgroup3 = LLVM::PoisonOp::create(rewriter, loc, v4i32);
4422 for (auto [sgpr, constant] : llvm::zip(sgprs, consts))
4423 dgroup3 =
4424 LLVM::InsertElementOp::create(rewriter, loc, dgroup3, sgpr, constant);
4425
4426 return dgroup3;
4427 }
4428
4429 Value getDGroup3Gather(DescriptorOp op, OpAdaptor &adaptor,
4430 ConversionPatternRewriter &rewriter, Location loc,
4431 ArrayRef<Value> consts) const {
4432 return getGatherIndices(op, adaptor, rewriter, loc, consts, false);
4433 }
4434
4435 LogicalResult
4436 matchAndRewrite(DescriptorOp op, OpAdaptor adaptor,
4437 ConversionPatternRewriter &rewriter) const override {
4438 if (!target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS))
4439 return op->emitOpError(
4440 "make_dma_descriptor is only supported on gfx1250");
4441
4442 Location loc = op.getLoc();
4443
4444 SmallVector<Value> consts;
4445 for (int64_t i = 0; i < 8; ++i)
4446 consts.push_back(createI32Constant(rewriter, loc, i));
4447
4448 Value dgroup0 = this->getDGroup0(adaptor);
4449 Value dgroup1 = this->getDGroup1(op, adaptor, rewriter, loc, consts);
4450 Value dgroup2 = this->getDGroup2(op, adaptor, rewriter, loc, consts);
4451 Value dgroup3 = this->getDGroup3(op, adaptor, rewriter, loc, consts);
4452 SmallVector<Value> results = {dgroup0, dgroup1, dgroup2, dgroup3};
4453 rewriter.replaceOpWithMultiple(op, {results});
4454 return success();
4455 }
4456};
4457
4458template <typename SourceOp, typename TargetOp>
4459struct AMDGPUTensorLoadStoreOpLowering
4460 : public ConvertOpToLLVMPattern<SourceOp> {
4461 using ConvertOpToLLVMPattern<SourceOp>::ConvertOpToLLVMPattern;
4463 AMDGPUTensorLoadStoreOpLowering(const LLVMTypeConverter &converter,
4464 const ROCDL::TargetInfo &target)
4465 : ConvertOpToLLVMPattern<SourceOp>(converter), target(target) {}
4466 ROCDL::TargetInfo target;
4467
4468 LogicalResult
4469 matchAndRewrite(SourceOp op, Adaptor adaptor,
4470 ConversionPatternRewriter &rewriter) const override {
4471 if (!target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS))
4472 return op->emitOpError("is only supported on gfx1250");
4473
4474 ValueRange desc = adaptor.getDesc();
4475 // Create a <v8 x i32> 0 as the fifth argument to match llvm intrinsic. It
4476 // will move into the TDM descriptor once it becomes relevant for future use
4477 auto v8i32 = VectorType::get(8, rewriter.getI32Type());
4478 Value dgroup4 = LLVM::ZeroOp::create(rewriter, op.getLoc(), v8i32);
4479 Attribute cachePolicy = rewriter.getI32IntegerAttr(0);
4480 rewriter.replaceOpWithNewOp<TargetOp>(op, desc[0], desc[1], desc[2],
4481 desc[3], dgroup4, cachePolicy,
4482 /*alias_scopes=*/nullptr,
4483 /*noalias_scopes=*/nullptr,
4484 /*tbaa=*/nullptr);
4485 return success();
4486 }
4487};
4488
4489struct GlobalPrefetchOpLowering
4490 : public ConvertOpToLLVMPattern<GlobalPrefetchOp> {
4491 GlobalPrefetchOpLowering(const LLVMTypeConverter &converter,
4492 const ROCDL::TargetInfo &target)
4493 : ConvertOpToLLVMPattern<GlobalPrefetchOp>(converter), target(target) {}
4494
4495 LogicalResult
4496 matchAndRewrite(GlobalPrefetchOp op, GlobalPrefetchOpAdaptor adaptor,
4497 ConversionPatternRewriter &rewriter) const override {
4498 if (!target.has(llvm::AMDGPU::FEAT_GFX1250_INSTS))
4499 return op->emitOpError("is only supported on gfx1250+");
4500
4501 const bool isSpeculative = op.getSpeculative();
4502 const int32_t immArgValue = getGlobalPrefetchLLVMEncoding(
4503 op.getTemporalHint(), op.getCacheScope(), isSpeculative);
4504 // amdgpu.global_prefetch is gfx1250+, so its policy bits use gfx12
4505 // encoding.
4506 Attribute cachePolicy = ROCDL::Gfx12CachePolicyAttr::get(
4507 rewriter.getContext(),
4508 static_cast<ROCDL::Gfx12CachePolicy>(immArgValue));
4509
4510 ValueRange indices = adaptor.getIndices();
4511 Value memRef = adaptor.getSrc();
4512 MemRefDescriptor descriptor(memRef);
4513 MemRefType memRefType = op.getSrc().getType();
4514 Location loc = op->getLoc();
4515 auto inboundsFlags = isSpeculative ? LLVM::GEPNoWrapFlags::none
4516 : LLVM::GEPNoWrapFlags::inbounds |
4517 LLVM::GEPNoWrapFlags::nuw;
4518 Value prefetchPtr = getStridedElementPtr(
4519 rewriter, loc, memRefType, descriptor, indices, inboundsFlags);
4520
4521 rewriter.replaceOpWithNewOp<ROCDL::GlobalPrefetchOp>(
4522 op, prefetchPtr, cachePolicy, mlir::ArrayAttr{}, mlir::ArrayAttr{},
4523 mlir::ArrayAttr{});
4524 return success();
4525 }
4526
4527private:
4528 ROCDL::TargetInfo target;
4529};
4530
4531struct ConvertAMDGPUToROCDLPass
4532 : public impl::ConvertAMDGPUToROCDLPassBase<ConvertAMDGPUToROCDLPass> {
4533 using Base::Base;
4534
4535 void runOnOperation() override {
4536 MLIRContext *ctx = &getContext();
4537 FailureOr<ROCDL::TargetInfo> targetInfo = ROCDL::TargetInfo::get(
4538 ROCDL::resolveArchOption(arch, chipset),
4539 /*waveSize=*/0, [&] { return emitError(UnknownLoc::get(ctx)); });
4540 if (failed(targetInfo))
4541 return signalPassFailure();
4542
4543 RewritePatternSet patterns(ctx);
4544 LLVMTypeConverter converter(ctx);
4545
4546 populateAMDGPUToROCDLConversionPatterns(converter, patterns, *targetInfo);
4548 LLVMConversionTarget target(getContext());
4549 target.addIllegalDialect<::mlir::amdgpu::AMDGPUDialect>();
4550 target.addLegalDialect<::mlir::LLVM::LLVMDialect>();
4551 target.addLegalDialect<::mlir::ROCDL::ROCDLDialect>();
4552 if (failed(applyPartialConversion(getOperation(), target,
4553 std::move(patterns))))
4554 signalPassFailure();
4555 }
4556};
4557} // namespace
4558
4560 TypeConverter &typeConverter) {
4562 typeConverter, [](gpu::AddressSpace space) {
4563 switch (space) {
4564 case gpu::AddressSpace::Global:
4565 return ROCDL::ROCDLDialect::kGlobalMemoryAddressSpace;
4566 case gpu::AddressSpace::Workgroup:
4567 return ROCDL::ROCDLDialect::kSharedMemoryAddressSpace;
4568 case gpu::AddressSpace::Private:
4569 return ROCDL::ROCDLDialect::kPrivateMemoryAddressSpace;
4570 case gpu::AddressSpace::Constant:
4571 return ROCDL::ROCDLDialect::kConstantMemoryAddressSpace;
4572 }
4573 llvm_unreachable("unknown address space enum value");
4574 });
4575 typeConverter.addConversion([](gpu::NamedBarrierType type) {
4576 return LLVM::LLVMPointerType::get(
4577 type.getContext(), ROCDL::ROCDLDialect::kBarrierAddressSpace);
4578 });
4579}
4580
4582 TypeConverter &typeConverter) {
4583 typeConverter.addTypeAttributeConversion(
4584 [](BaseMemRefType type, amdgpu::AddressSpaceAttr as)
4585 -> TypeConverter::AttributeConversionResult {
4586 MLIRContext *ctx = as.getContext();
4587 Type i64 = IntegerType::get(ctx, 64);
4588 switch (as.getValue()) {
4589 case amdgpu::AddressSpace::FatRawBuffer:
4590 return IntegerAttr::get(i64, 7);
4591 case amdgpu::AddressSpace::BufferRsrc:
4592 return IntegerAttr::get(i64, 8);
4593 case amdgpu::AddressSpace::FatStructuredBuffer:
4594 return IntegerAttr::get(i64, 9);
4595 }
4596 return TypeConverter::AttributeConversionResult::abort();
4597 });
4598 typeConverter.addConversion([&](DsBarrierStateType type) -> Type {
4599 return IntegerType::get(type.getContext(), 64);
4600 });
4601 typeConverter.addConversion([&](TDMBaseType type) -> Type {
4602 Type i32 = IntegerType::get(type.getContext(), 32);
4603 return typeConverter.convertType(VectorType::get(4, i32));
4604 });
4605 typeConverter.addConversion([&](TDMGatherBaseType type) -> Type {
4606 Type i32 = IntegerType::get(type.getContext(), 32);
4607 return typeConverter.convertType(VectorType::get(4, i32));
4608 });
4609 typeConverter.addConversion(
4610 [&](TDMDescriptorType type,
4611 SmallVectorImpl<Type> &result) -> std::optional<LogicalResult> {
4612 Type i32 = IntegerType::get(type.getContext(), 32);
4613 Type v4i32 = typeConverter.convertType(VectorType::get(4, i32));
4614 Type v8i32 = typeConverter.convertType(VectorType::get(8, i32));
4615 llvm::append_values(result, v4i32, v8i32, v4i32, v4i32);
4616 return success();
4617 });
4618
4619 auto addUnrealizedCast = [](OpBuilder &builder, TypeRange types,
4620 ValueRange inputs,
4622 // Only create unrealized_conversion_cast for TDMDescriptorType.
4623 // All other types which are not expected, should be
4624 // materialized by other target materialization functions.
4625 if (inputs.size() != 1)
4626 return {};
4627
4628 if (!isa<TDMDescriptorType>(inputs[0].getType()))
4629 return {};
4630
4631 auto cast = UnrealizedConversionCastOp::create(builder, loc, types, inputs);
4632 return cast.getResults();
4633 };
4634
4635 typeConverter.addTargetMaterialization(addUnrealizedCast);
4636}
4637
4639 LLVMTypeConverter &converter, RewritePatternSet &patterns,
4640 const ROCDL::TargetInfo &target) {
4642 patterns
4643 .add<FatRawBufferCastLowering,
4644 RawBufferOpLowering<RawBufferLoadOp, ROCDL::RawPtrBufferLoadOp>,
4645 RawBufferOpLowering<RawBufferStoreOp, ROCDL::RawPtrBufferStoreOp>,
4646 RawBufferOpLowering<RawBufferAtomicFaddOp,
4647 ROCDL::RawPtrBufferAtomicFaddOp>,
4648 RawBufferOpLowering<RawBufferAtomicFmaxOp,
4649 ROCDL::RawPtrBufferAtomicFmaxOp>,
4650 RawBufferOpLowering<RawBufferAtomicSmaxOp,
4651 ROCDL::RawPtrBufferAtomicSmaxOp>,
4652 RawBufferOpLowering<RawBufferAtomicUminOp,
4653 ROCDL::RawPtrBufferAtomicUminOp>,
4654 RawBufferOpLowering<RawBufferAtomicCmpswapOp,
4655 ROCDL::RawPtrBufferAtomicCmpSwap>,
4656 AMDGPUDPPLowering, MemoryCounterWaitOpLowering, LDSBarrierOpLowering,
4657 SchedBarrierOpLowering, MFMAOpLowering, ScaledMFMAOpLowering,
4658 SparseMFMAOpLowering, WMMAOpLowering, ScaledWMMAOpLowering,
4659 SparseWMMAOpLowering, DotOpLowering, ExtPackedFp8OpLowering,
4660 ScaledExtPackedMatrixOpLowering, ScaledExtPackedOpLowering,
4661 PackedScaledTruncOpLowering, PackedTrunc2xFp8OpLowering,
4662 PackedStochRoundFp8OpLowering, GatherToLDSOpLowering,
4663 GlobalLoadAsyncToLDSOpLowering, TransposeLoadOpLowering,
4664 GlobalTransposeLoadOpLowering, AMDGPUPermlaneLowering,
4665 AMDGPUPermlaneVarLowering, AMDGPUMakeDmaBaseLowering<MakeDmaBaseOp>,
4666 AMDGPUMakeDmaBaseLowering<MakeGatherDmaBaseOp>,
4667 AMDGPULowerDescriptor<MakeDmaDescriptorOp>,
4668 AMDGPULowerDescriptor<MakeGatherDmaDescriptorOp>,
4669 AMDGPUTensorLoadStoreOpLowering<TensorLoadToLDSOp,
4670 ROCDL::TensorLoadToLDSOp>,
4671 AMDGPUTensorLoadStoreOpLowering<TensorStoreFromLDSOp,
4672 ROCDL::TensorStoreFromLDSOp>,
4673 DsBarrierInitOpLowering, DsBarrierPollStateOpLowering,
4674 DsAsyncBarrierArriveOpLowering, DsBarrierArriveOpLowering,
4675 GlobalPrefetchOpLowering>(converter, target);
4676 patterns.add<AMDGPUSwizzleBitModeLowering, DsBarrierStatePhaseOpLowering,
4677 DsBarrierStatePendingCountOpLowering,
4678 DsBarrierStateInitCountOpLowering,
4679 DsBarrierStatePhaseParityLowering>(converter);
4680}
static std::optional< StringRef > wmmaOpToIntrinsicRDNA(Type elemSourceType, Type elemBSourceType, Type elemDestType, uint32_t k, bool isRDNA3)
Returns the rocdl intrinsic corresponding to a WMMA operation wmma for RDNA3/4 architectures.
static std::optional< StringRef > wmmaOpToIntrinsic(WMMAOp wmma, const ROCDL::TargetInfo &target)
Returns the rocdl intrinsic corresponding to a WMMA operation wmma if one exists.
static bool typeIsExpectedFp8ForTarget(const ROCDL::TargetInfo &target, Type type)
Return true if type is the E4M3FN variant of an 8-bit float that is supported by the _fp8 instruction...
static Value convertUnsignedToInt(ConversionPatternRewriter &rewriter, Location loc, Value val, unsigned width)
Zero-extend or truncate the unsigned number val to width bits.
static void wmmaPushInputOperand(ConversionPatternRewriter &rewriter, Location loc, const TypeConverter *typeConverter, bool isUnsigned, Value llvmInput, Value mlirInput, SmallVectorImpl< Value > &operands, SmallVectorImpl< NamedAttribute > &attrs, StringRef attrName)
Push an input operand.
static Value castScaleOperand(ConversionPatternRewriter &rewriter, Location loc, Value input)
Converts the scaled MFMA/WMMA operands, scalesA and scalesB, from MLIR AMDGPU dialect convention to R...
static std::optional< StringRef > getScaledWmmaIntrinsicName(int64_t m, int64_t n, int64_t k, bool isScale16)
Determines the ROCDL intrinsic name for scaled WMMA based on dimensions and scale block size (16 or 3...
static void wmmaPushOutputOperand(ConversionPatternRewriter &rewriter, Location loc, const TypeConverter *typeConverter, Value output, int32_t subwordOffset, bool clamp, SmallVectorImpl< Value > &operands, SmallVectorImpl< NamedAttribute > &attrs)
Push the output operand.
static std::optional< StringRef > smfmacOpToIntrinsic(SparseMFMAOp op, const ROCDL::TargetInfo &target)
Returns the rocdl intrinsic corresponding to a SparseMFMA (smfmac) operation if one exists.
static Value createI64Constant(ConversionPatternRewriter &rewriter, Location loc, int64_t value)
static Value getNumRecords(ConversionPatternRewriter &rewriter, Location loc, MemRefType memrefType, MemRefDescriptor &memrefDescriptor, ArrayRef< int64_t > strides, int64_t elementByteWidth, unsigned numRecordsWidth, bool boundsCheck)
Compute the contents of the num_records field for a given memref descriptor - that is,...
static std::optional< StringRef > wmmaOpToIntrinsicGfx1250(Type elemSourceType, Type elemBSourceType, Type elemDestType, uint32_t k)
Return the rocdl intrinsic corresponding to a WMMA operation wmma for the gfx1250 architecture.
static Value packSmallFloatVectorOperand(ConversionPatternRewriter &rewriter, Location loc, Value input, bool allowBf16=true)
Pack small float vector operands (fp4/fp6/fp8/bf16) into the format expected by scaled matrix multipl...
static bool typeIsExpectedBf8ForTarget(const ROCDL::TargetInfo &target, Type type)
Return true if type is the E5M2 variant of an 8-bit float that is supported by the _bf8 instructions ...
static std::optional< ROCDL::WMMAMatrixScaleFormat > getWmmaScaleFormat(Type elemType)
Maps f8 scale element types to WMMA scale format codes.
static Value convertPackedVectorOperand(ConversionPatternRewriter &rewriter, Location loc, Value input, bool allowBf16=true)
Converts packed vector operands to the expected ROCDL types.
static Value getLinearIndexI32(ConversionPatternRewriter &rewriter, Location loc, MemRefDescriptor &memRefDescriptor, ValueRange indices, ArrayRef< int64_t > strides)
Returns the linear index used to access an element in the memref.
static Value convertUnsignedToI32(ConversionPatternRewriter &rewriter, Location loc, Value val)
Convert an unsigned number val to i32.
static std::optional< StringRef > mfmaOpToIntrinsic(MFMAOp mfma, const ROCDL::TargetInfo &target)
Return the rocdl intrinsic corresponding to a MFMA operation mfma if one exists.
static Value createI32Constant(ConversionPatternRewriter &rewriter, Location loc, int32_t value)
static std::optional< ROCDL::MatrixFormat > smallFloatTypeToMatrixFormat(Type mlirElemType)
static std::optional< SparseWMMAOpInfo > sparseWMMAOpToIntrinsic(SparseWMMAOp swmmac, const ROCDL::TargetInfo &target)
std::tuple< StringRef, ROCDL::MatrixFormat, ROCDL::MatrixFormat > ScaledMFMAIntrinsic
If there is a scaled MFMA instruction for the input element types aType and bType,...
static std::optional< ScaledMFMAIntrinsic > mfmaOpToScaledIntrinsic(Type aType, Type bType, Type destType, uint32_t m, uint32_t n, uint32_t k, uint32_t b, const ROCDL::TargetInfo &target)
static Value convertUnsignedToI64(ConversionPatternRewriter &rewriter, Location loc, Value val)
Convert an unsigned number val to i64.
static Value makeBufferRsrc(ConversionPatternRewriter &rewriter, Location loc, Value basePointer, Value numRecords, bool boundsCheck, const ROCDL::TargetInfo &target, unsigned numRecordsWidth, Value cacheSwizzleStride=nullptr, unsigned addressSpace=8)
return success()
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
ArrayAttr()
b getContext())
auto load
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
static constexpr unsigned kSizePosInMemRefDescriptor
static constexpr unsigned kStridePosInMemRefDescriptor
static constexpr unsigned kOffsetPosInMemRefDescriptor
static constexpr unsigned kAllocatedPtrPosInMemRefDescriptor
static constexpr unsigned kAlignedPtrPosInMemRefDescriptor
static Value clamp(ImplicitLocOpBuilder &builder, Value value, Value lowerBound, Value upperBound)
Attributes are known-constant values of operations.
Definition Attributes.h:25
This class provides a shared interface for ranked and unranked memref types.
Utility class for operation conversions targeting the LLVM dialect that match exactly one source oper...
Definition Pattern.h:233
ConvertOpToLLVMPattern(const LLVMTypeConverter &typeConverter, PatternBenefit benefit=1)
Definition Pattern.h:239
typename SourceOp::template GenericAdaptor< ArrayRef< ValueRange > > OneToNOpAdaptor
Definition Pattern.h:236
typename SourceOp::Adaptor OpAdaptor
Definition Pattern.h:235
Value getStridedElementPtr(ConversionPatternRewriter &rewriter, Location loc, MemRefType type, Value memRefDesc, ValueRange indices, LLVM::GEPNoWrapFlags noWrapFlags=LLVM::GEPNoWrapFlags::none) const
Convenience wrapper for the corresponding helper utility.
Definition Pattern.cpp:71
llvm::TypeSize getTypeSizeInBits(Type t) const
Returns the size in bits of the given type in the current scope.
Conversion from types to the LLVM IR dialect.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:64
Helper class to produce LLVM dialect operations extracting or inserting elements of a MemRef descript...
Value stride(OpBuilder &builder, Location loc, unsigned pos)
Builds IR extracting the pos-th size from the descriptor.
Value size(OpBuilder &builder, Location loc, unsigned pos)
Builds IR extracting the pos-th size from the descriptor.
NamedAttribute represents a combination of a name and an Attribute value.
Definition Attributes.h:164
This class helps build Operations.
Definition Builders.h:210
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:432
result_range getResults()
Definition Operation.h:440
unsigned getNumResults()
Return the number of results held by this operation.
Definition Operation.h:429
Describes the AMDGPU target a lowering is producing code for: the triple's subarch (which identifies ...
static FailureOr< TargetInfo > get(StringRef arch, unsigned waveSize=0, function_ref< InFlightDiagnostic()> emitError=nullptr)
Resolves a target description.
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
This class provides an abstraction over the various different ranges of value types.
Definition TypeRange.h:40
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isF64() const
Definition Types.cpp:41
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
Definition Types.cpp:35
bool isF8E5M2() const
Definition Types.cpp:45
bool isSignedInteger() const
Return true if this is a signed integer type (with the specified width).
Definition Types.cpp:78
bool isF8E4M3FN() const
Definition Types.cpp:44
bool isF32() const
Definition Types.cpp:40
bool isUnsignedInteger() const
Return true if this is an unsigned integer type (with the specified width).
Definition Types.cpp:90
bool isInteger() const
Return true if this is an integer type (with the specified width).
Definition Types.cpp:58
bool isF16() const
Definition Types.cpp:38
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
Definition Types.cpp:124
bool isBF16() const
Definition Types.cpp:37
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
Value getStridedElementPtr(OpBuilder &builder, Location loc, const LLVMTypeConverter &converter, MemRefType type, Value memRefDesc, ValueRange indices, LLVM::GEPNoWrapFlags noWrapFlags=LLVM::GEPNoWrapFlags::none)
Performs the index computation to get to the element at indices of the memory pointed to by memRefDes...
Definition Pattern.cpp:620
LogicalResult decomposeValue(OpBuilder &builder, Location loc, Value src, Type dstType, SmallVectorImpl< Value > &result, bool permitVariablySizedScalars=false)
Decomposes a src value into a set of values of type dstType through series of bitcasts and vector ops...
Definition Pattern.cpp:512
Value createIndexAttrConstant(OpBuilder &builder, Location loc, Type resultType, int64_t value)
Creates an llvm.mlir.constant producing value as resultType, which is expected to be the converted in...
Definition Pattern.cpp:58
Value composeValue(OpBuilder &builder, Location loc, ValueRange src, Type dstType)
Composes a set of src values into a single value of type dstType through series of bitcasts and vecto...
Definition Pattern.cpp:611
StringRef resolveArchOption(StringRef arch, StringRef deprecatedAlias)
Returns the target architecture that a pass should parse, given its arch option and the value of the ...
int32_t getGlobalPrefetchLLVMEncoding(amdgpu::LoadTemporalHint hint, amdgpu::Scope scope, bool isSpeculative)
Definition AMDGPUEnums.h:18
void populateCommonGPUTypeAndAttributeConversions(TypeConverter &typeConverter)
Remap common GPU memory spaces (Workgroup, Private, etc) to LLVM address spaces.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:734
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
Definition Matchers.h:490
SmallVector< OpFoldResult > getMixedValues(ArrayRef< int64_t > staticValues, ValueRange dynamicValues, MLIRContext *context)
Return a vector of OpFoldResults with the same size a staticValues, but all elements for which Shaped...
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:311
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
detail::constant_int_predicate_matcher m_Zero()
Matches a constant scalar / vector splat / tensor splat integer zero.
Definition Matchers.h:442
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
void populateAMDGPUToROCDLConversionPatterns(LLVMTypeConverter &converter, RewritePatternSet &patterns, const ROCDL::TargetInfo &target)
Note: This function will also add conversions for the AMDGPU-specific address spaces and types,...
void populateGpuMemorySpaceAttributeConversions(TypeConverter &typeConverter, const MemorySpaceMapping &mapping)
Populates memory space attribute conversion rules for lowering gpu.address_space to integer values.
llvm::TypeSwitch< T, ResultT > TypeSwitch
Definition LLVM.h:147
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
void populateAMDGPUTypeAndAttributeConversions(TypeConverter &typeConverter)
Remap AMDGPU memory spaces to LLVM address spaces by mapping amdgpu::AddressSpace::fat_raw_buffer to ...
Returns the rocdl intrinsic corresponding to a SparseWMMA operation swmmac if one exists.