MLIR 23.0.0git
uArchBase.h
Go to the documentation of this file.
1//===- uArch.h --------------------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// \file
10// Base uArch definition for different architectures, plus the SPIRV / Khronos
11// OpenCL extension instruction defaults shared across Intel Xe uArchs.
12//
13//===----------------------------------------------------------------------===//
14#ifndef MLIR_DIALECT_XEGPU_UARCH_UARCHBASE_H
15#define MLIR_DIALECT_XEGPU_UARCH_UARCHBASE_H
16
17#include <cassert>
18#include <optional>
19#include <tuple>
20#include <utility>
21
24#include "mlir/IR/Types.h"
25#include "llvm/ADT/DenseMap.h"
26#include "llvm/ADT/STLExtras.h"
27#include "llvm/ADT/SmallVector.h"
28#include "llvm/ADT/StringRef.h"
29#include "llvm/Support/Casting.h"
30#include "llvm/Support/DebugLog.h"
31#include "llvm/Support/ErrorHandling.h"
32
33namespace mlir {
34namespace xegpu {
35namespace uArch {
36
37// An enum class to represent the scope of an instruction
39enum class InstructionKind {
40 SubgroupMatrixMultiplyAcc, // Dot Product Accumulate Systolic (DPAS) is a
41 // matrix multiply-add operation
42 SubgroupScaledMatrixMultiplyAcc, // Scaled Matrix Multiply Accumulate is a
43 // DPAS with scaling factor applied to
44 // operand A or B before multiplication
45 Subgroup2DBlockStore, // Subgroup-level 2D block write instruction
46 Subgroup2DBlockLoad, // Subgroup-level 2D block load instruction
47 Subgroup2DBlockPrefetch, // Subgroup-level 2D block prefetch instruction
48 StoreScatter, // Lane-level store (scalar, vector)
49 LoadGather, // Lane-level load (scalar, vector)
50};
51
52// A struct to represent basic information about an instruction.
53// The primary purpose of the Instruction struct is to provide a generic way to
54// represent information about an instruction and to use this information to
55// generate the uArch. Specifc instruction in a uArch can inherit from this
56// struct and add more fields as needed.
60
61 ~Instruction() = default;
62 // Get methods
64 InstructionScope getScope() const { return scope; }
65 static llvm::StringRef toString(InstructionKind instKind) {
66 switch (instKind) {
68 return "dpas";
70 return "dpas_mx";
72 return "store_nd";
74 return "load_nd";
76 return "prefetch_nd";
78 return "store";
80 return "load";
81 }
82 llvm_unreachable("Unknown InstructionKind");
83 }
84
85protected:
86 const InstructionKind instKind; // Specific InstructionKind (e.g., DPAS)
87 const InstructionScope scope; // scope of the instruction (e.g., lane,
88 // subgroup, workgroup, cluster)
89};
90
91struct uArch {
92 enum class Kind {
93 // Xe2 family
101 };
102
103 // Constructor
105 : kind(kind) {
106 for (const Instruction *instr : instructionRegistry)
107 this->instructionRegistry[instr->getInstructionKind()] = instr;
108 }
109 virtual ~uArch() = default;
110 Kind getKind() const { return kind; }
111
112 virtual int getSubgroupSize() const = 0;
113 virtual unsigned getGeneralPackedFormatBitSize() const = 0;
114
116 auto it = instructionRegistry.find(instKind);
117 assert(it != instructionRegistry.end() &&
118 "Instruction not found in registry");
119 return it->second;
120 }
121
123 return instructionRegistry.contains(instr);
124 }
125
126protected:
128 llvm::SmallDenseMap<InstructionKind, const Instruction *, 32>
130};
131
132//===----------------------------------------------------------------------===//
133// Interfaces
134//===----------------------------------------------------------------------===//
137 // Get supported Matrix shapes
139 getSupportedShapes(Type dataType, MMAOpndKind matrixType) = 0;
140 // @TODO: This method takes an context object as a parameter, this is to
141 // create the Type objects from the same context. Since type objects are
142 // uniqued in a specific context, to do things like "aType == bType" (where
143 // aType and bType are both same type) kind of checks, the both types should
144 // be from the same context.
145 //
146 // One alternative to this is to create enum to represent each types, but this
147 // adds an extra burden to user to convert these enums to specific types. In
148 // fact the utility that would convert enumToType() and vice versa would still
149 // have to use the context object.
150 //
151 // Untill we have a better solution, we stick to passing context object to
152 // this method.
154 getSupportedTypes(MLIRContext &context, MMAOpndKind matrixType) = 0;
155
159 virtual bool isLaneLayoutRowMajorOrder() const = 0;
160 virtual ~MMAInstructionInterface() = default;
161};
162
163// Interface for subgroup-level 2D block instructions (load / store / prefetch).
164// All three describe the set of hardware-supported block shapes via
165// (width, height, count) tuples and share a packed-format bit size. The
166// transform / transpose / upConv flags are only meaningful for loads; store
167// and prefetch implementations ignore them.
170 std::tuple<llvm::ArrayRef<int>, llvm::ArrayRef<int>, llvm::ArrayRef<int>>;
171
172 // Returns the supported (widths, heights, counts) for the given element
173 // type, or std::nullopt if the element type is unsupported.
174 std::optional<BlockShapes>
175 getBlockWidthHeightCount(Type elemTy, bool hasTransform = false,
176 bool hasTranspose = false,
177 bool upConv = false) const {
178 return computeBlockWidthHeightCount(elemTy, hasTransform, hasTranspose,
179 upConv);
180 }
181
182 // Bit size of the packed format used by this block instruction.
183 virtual int32_t getPackedFormatBitSize() const = 0;
184 virtual ~BlockIOInstructionInterface() = default;
185
186protected:
187 virtual std::optional<BlockShapes>
188 computeBlockWidthHeightCount(Type elemTy, bool hasTransform,
189 bool hasTranspose, bool upConv) const = 0;
190};
191
192//===----------------------------------------------------------------------===//
193// Common virtual ISA instructions (shared across architectures)
194//===----------------------------------------------------------------------===//
195
196//===----------------------------------------------------------------------===//
197// SPIRV
198//===----------------------------------------------------------------------===//
199template <InstructionKind Kind>
201 static_assert(Kind == InstructionKind::LoadGather ||
203 "ScatterIO only supports LoadGather / StoreScatter");
204
206
207 static bool classof(const Instruction *B) {
208 return B->getInstructionKind() == Kind;
209 }
210
211 virtual int32_t getMaxLaneAccessSizeBytes() const = 0;
213};
215 : public ScatterIoInstructionInterface<InstructionKind::LoadGather> {
216 int32_t getMaxLaneAccessSizeBytes() const override { return 16; }
217};
218
220 : public ScatterIoInstructionInterface<InstructionKind::StoreScatter> {
221 int32_t getMaxLaneAccessSizeBytes() const override { return 16; }
222};
223
224//===----------------------------------------------------------------------===//
225// SPIRV / OpenCL-extension subgroup instructions
226//
227// These come from cl_intel_subgroup_2d_block_io and
228// cl_intel_subgroup_matrix_multiply_accumulate. A uArch only needs to
229// subclass when it diverges from the extension defaults.
230//===----------------------------------------------------------------------===//
231
237 static bool classof(const Instruction *B) {
238 return B->getInstructionKind() == InstructionKind::Subgroup2DBlockStore;
239 }
240 // Source :
241 // https://registry.khronos.org/OpenCL/extensions/intel/cl_intel_subgroup_2d_block_io.html#_add_a_new_section_5_2_x_cl_intel_subgroup_2d_block_io
242 // Stores ignore the transform / transpose / upConv flags.
243 int32_t getPackedFormatBitSize() const override { return 16; }
244
245protected:
246 std::optional<BlockShapes>
247 computeBlockWidthHeightCount(Type elemTy, bool /*hasTransform*/,
248 bool /*hasTranspose*/,
249 bool /*upConv*/) const override {
250 static const int kHeight[] = {1, 2, 4, 8};
251 static const int kWidth16[] = {16};
252 static const int kCount[] = {1};
253 const int elemByteSize = elemTy.getIntOrFloatBitWidth() / 8;
254 if (elemByteSize == 1 || elemByteSize == 2 || elemByteSize == 4)
255 return std::make_tuple(llvm::ArrayRef<int>(kWidth16),
256 llvm::ArrayRef<int>(kHeight),
257 llvm::ArrayRef<int>(kCount));
258 return std::nullopt;
259 }
260};
261
267 static bool classof(const Instruction *B) {
268 return B->getInstructionKind() == InstructionKind::Subgroup2DBlockLoad;
269 }
270
271 // Source :
272 // https://registry.khronos.org/OpenCL/extensions/intel/cl_intel_subgroup_2d_block_io.html#_add_a_new_section_5_2_x_cl_intel_subgroup_2d_block_io
273 int32_t getPackedFormatBitSize() const override { return 16; }
274
275protected:
276 std::optional<BlockShapes>
277 computeBlockWidthHeightCount(Type elemTy, bool hasTransform,
278 bool hasTranspose, bool upConv) const override {
279 static const int kHeightAtLeast1[] = {1, 2, 4, 8, 16, 32};
280 static const int kHeightAtLeast8[] = {8, 16, 32};
281 static const int kHeightAtLeast16[] = {16, 32};
282 static const int kHeight32[] = {32};
283 static const int kHeight64[] = {64};
284
285 static const int kWidth64[] = {64};
286 static const int kWidth32[] = {32};
287 static const int kWidth16[] = {16};
288 static const int kWidthAtLeast16[] = {16, 32};
289 static const int kWidthAtLeast32[] = {32, 64};
290 static const int kWidth8[] = {8};
291
292 static const int32_t kCount1[] = {1};
293 static const int32_t kCount2[] = {1, 2};
294 static const int32_t kCount4[] = {1, 2, 4};
295 static const int32_t kCount4Only[] = {4};
296 // (elemBits, transform, transpose, upConvert)
297 using Key = std::tuple<int, uint8_t, uint8_t, uint8_t>;
298 // (widths, heights, counts)
299 using Value = std::tuple<llvm::ArrayRef<int32_t>, llvm::ArrayRef<int32_t>,
301 // The table is keyed on element bit width so sub-byte elements can be
302 // expressed directly. 4-bit elements are packed two-per-byte, so their
303 // widths (or heights, when transformed) are double the 8-bit rows.
304 static const llvm::DenseMap<Key, Value> kMap = {
305 {{8, false, false, false}, {kWidthAtLeast16, kHeightAtLeast1, kCount2}},
306 {{8, false, false, true}, {kWidth16, kHeightAtLeast8, kCount4Only}},
307 {{16, false, false, false}, {kWidth16, kHeightAtLeast1, kCount2}},
308 {{32, false, false, false}, {kWidth16, kHeightAtLeast1, kCount1}},
309 // Block Loads with Transform:
310 {{8, true, false, false}, {kWidth16, kHeight32, kCount4}},
311 {{16, true, false, false}, {kWidth16, kHeightAtLeast16, kCount2}},
312 // Block Loads with Transpose:
313 {{8, false, true, false}, {kWidth32, kHeightAtLeast16, kCount1}},
314 {{16, false, true, false}, {kWidth16, kHeightAtLeast16, kCount1}},
315 {{32, false, true, false}, {kWidth8, kHeightAtLeast16, kCount1}},
316 // 4-bit elements (sub-byte):
317 {{4, false, false, false}, {kWidthAtLeast32, kHeightAtLeast1, kCount2}},
318 {{4, false, false, true}, {kWidth32, kHeightAtLeast8, kCount4Only}},
319 {{4, true, false, false}, {kWidth16, kHeight64, kCount4}},
320 {{4, false, true, false}, {kWidth64, kHeightAtLeast16, kCount1}}};
321 int elemBitSize = elemTy.getIntOrFloatBitWidth();
322 auto it = kMap.find({elemBitSize, hasTransform, hasTranspose, upConv});
323 if (it != kMap.end())
324 return it->second;
325 return std::nullopt;
326 }
327};
328
334 static bool classof(const Instruction *B) {
335 return B->getInstructionKind() == InstructionKind::Subgroup2DBlockPrefetch;
336 }
337 // Source :
338 // https://registry.khronos.org/OpenCL/extensions/intel/cl_intel_subgroup_buffer_prefetch.html#_add_a_new_section_6_15_x_sub_group_prefetch_functions
339 // Prefetches ignore the transform / transpose / upConv flags.
340 int32_t getPackedFormatBitSize() const override { return 16; }
341
342protected:
343 std::optional<BlockShapes>
344 computeBlockWidthHeightCount(Type elemTy, bool /*hasTransform*/,
345 bool /*hasTranspose*/,
346 bool /*upConv*/) const override {
347 static const int kHeightAtLeast1[] = {1, 2, 4, 8, 16, 32};
348
349 static const int kWidth32[] = {32};
350 static const int kWidth16[] = {16};
351
352 static const int32_t kCount1[] = {1};
353 static const int32_t kCount2[] = {1, 2};
354 // elemBytes
355 using Key = int;
356 // (widths, heights, counts)
357 using Value = std::tuple<llvm::ArrayRef<int32_t>, llvm::ArrayRef<int32_t>,
359 static const llvm::DenseMap<Key, Value> kMap = {
360 {1, {kWidth32, kHeightAtLeast1, kCount2}},
361 {2, {kWidth16, kHeightAtLeast1, kCount2}},
362 {4, {kWidth16, kHeightAtLeast1, kCount1}},
363 };
364 const int elemByteSize = elemTy.getIntOrFloatBitWidth() / 8;
365 auto it = kMap.find(elemByteSize);
366 if (it != kMap.end())
367 return it->second;
368 return std::nullopt;
369 }
370};
371
380 static bool classof(const Instruction *B) {
381 return B->getInstructionKind() ==
383 }
384 // Source:
385 // https://registry.khronos.org/OpenCL/extensions/intel/cl_intel_subgroup_matrix_multiply_accumulate.html
386
388 getSupportedShapes(Type dataType, MMAOpndKind matrixType) override;
390 MMAOpndKind matrixType) override;
391
395
398 bool isLaneLayoutRowMajorOrder() const override { return true; }
399
400protected:
401 const unsigned packedFormatBitSizeA;
402 const unsigned packedFormatBitSizeB;
403};
404
413 static bool classof(const Instruction *B) {
414 return B->getInstructionKind() ==
416 }
417 // Source:
418 // https://github.com/intel/llvm/blob/sycl/sycl/doc/design/spirv-extensions/SPV_INTEL_subgroup_scaled_matrix_multiply_accumulate.asciidoc
419
421 getSupportedShapes(Type dataType, MMAOpndKind matrixType) override;
423 MMAOpndKind matrixType) override;
424
428
431 bool isLaneLayoutRowMajorOrder() const override { return true; }
432
433protected:
434 const unsigned packedFormatBitSizeA;
435 const unsigned packedFormatBitSizeB;
436};
437
438//===----------------------------------------------------------------------===//
439// Inline implementations
440//===----------------------------------------------------------------------===//
441
442namespace util {
447 for (unsigned x : a)
448 for (unsigned y : b)
449 result.emplace_back(x, y);
450 return result;
451}
452} // namespace util
453
456 MMAOpndKind matrixType) {
457 auto M = getSupportedM(dataType);
458 auto K = getSupportedK(dataType);
459 auto N = getSupportedN(dataType);
460 switch (matrixType) {
462 return util::crossProduct(M, K);
464 return util::crossProduct(K, N);
467 return util::crossProduct(M, N);
468 }
469 return {};
470}
471
474 MMAOpndKind matrixType) {
475 Type bf16Type = BFloat16Type::get(&context);
476 Type f16Type = Float16Type::get(&context);
477 Type tf32Type = FloatTF32Type::get(&context);
478 Type f32Type = Float32Type::get(&context);
479
480 switch (matrixType) {
483 return {bf16Type, f16Type, tf32Type};
486 return {bf16Type, f16Type, f32Type};
487 }
488 return {};
489}
490
493 return {1, 2, 3, 4, 5, 6, 7, 8};
494}
495
498 assert(type.isIntOrFloat() && "Matrix type must be int or float");
499 auto bitWidth = type.getIntOrFloatBitWidth();
500 uint32_t kSize = 0;
501 switch (bitWidth) {
502 case 4:
503 kSize = 64;
504 break;
505 case 8:
506 kSize = 32;
507 break;
508 case 16:
509 kSize = 16;
510 break;
511 case 32:
512 kSize = 8;
513 break;
514 default:
515 llvm_unreachable("Invalid int or float");
516 }
517 return {kSize};
518}
519
522 return {16};
523}
524
527 MMAOpndKind matrixType) {
528 // Avoid calling getSupportedK for C/D types (which are f32/bf16
529 // and not valid for the K-dimension bit-width calculation).
530 switch (matrixType) {
532 return util::crossProduct(getSupportedM(dataType), getSupportedK(dataType));
534 return util::crossProduct(getSupportedK(dataType), getSupportedN(dataType));
537 return util::crossProduct(getSupportedM(dataType), getSupportedN(dataType));
538 }
539 return {};
540}
541
544 MMAOpndKind matrixType) {
545 Type f8E4M3FNType = Float8E4M3FNType::get(&context);
546 Type f8E5M2Type = Float8E5M2Type::get(&context);
547 Type f4E2M1FNType = Float4E2M1FNType::get(&context);
548 Type bf16Type = BFloat16Type::get(&context);
549 Type f32Type = Float32Type::get(&context);
550
551 switch (matrixType) {
554 return {f8E4M3FNType, f8E5M2Type, f4E2M1FNType};
557 return {bf16Type, f32Type};
558 }
559 return {};
560}
561
564 return {8};
565}
566
569 assert(type.isIntOrFloat() && "Matrix type must be int or float");
570 auto bitWidth = type.getIntOrFloatBitWidth();
571 switch (bitWidth) {
572 case 4:
573 return {64}; // FP4: scale K by 4 (base 16-bit K=16 -> 64)
574 case 8:
575 return {32}; // FP8: scale K by 2 (base 16-bit K=16 -> 32)
576 default:
577 // Scaled dpas only supports FP8 (8-bit) and FP4 (4-bit) types for A/B
578 // matrices. Return empty so callers can gracefully reject unsupported
579 // types instead of aborting.
580 return {};
581 }
582}
583
586 return {16};
587}
588
589} // namespace uArch
590} // namespace xegpu
591} // namespace mlir
592
593#endif // MLIR_DIALECT_XEGPU_UARCH_UARCHBASE_H
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isIntOrFloat() const
Return true if this is an integer (of any signedness) or a float type.
Definition Types.cpp:118
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
Definition Types.cpp:124
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
llvm::SmallVector< std::pair< uint32_t, uint32_t >, 16 > crossProduct(const llvm::SmallVector< uint32_t, 8 > &a, const llvm::SmallVector< uint32_t, 8 > &b)
Definition uArchBase.h:444
Include the generated interface declarations.
std::tuple< llvm::ArrayRef< int >, llvm::ArrayRef< int >, llvm::ArrayRef< int > > BlockShapes
Definition uArchBase.h:169
virtual int32_t getPackedFormatBitSize() const =0
virtual std::optional< BlockShapes > computeBlockWidthHeightCount(Type elemTy, bool hasTransform, bool hasTranspose, bool upConv) const =0
std::optional< BlockShapes > getBlockWidthHeightCount(Type elemTy, bool hasTransform=false, bool hasTranspose=false, bool upConv=false) const
Definition uArchBase.h:175
Instruction(InstructionKind kind, InstructionScope scope)
Definition uArchBase.h:58
const InstructionScope scope
Definition uArchBase.h:87
InstructionScope getScope() const
Definition uArchBase.h:64
static llvm::StringRef toString(InstructionKind instKind)
Definition uArchBase.h:65
InstructionKind getInstructionKind() const
Definition uArchBase.h:63
const InstructionKind instKind
Definition uArchBase.h:86
int32_t getMaxLaneAccessSizeBytes() const override
Definition uArchBase.h:216
virtual llvm::SmallVector< std::pair< uint32_t, uint32_t >, 16 > getSupportedShapes(Type dataType, MMAOpndKind matrixType)=0
virtual llvm::SmallVector< uint32_t, 8 > getSupportedN(Type type) const =0
virtual bool isLaneLayoutRowMajorOrder() const =0
virtual llvm::SmallVector< uint32_t, 8 > getSupportedK(Type type) const =0
virtual llvm::SmallVector< uint32_t, 8 > getSupportedM(Type type) const =0
virtual llvm::SmallVector< Type, 8 > getSupportedTypes(MLIRContext &context, MMAOpndKind matrixType)=0
virtual int32_t getMaxLaneAccessSizeBytes() const =0
static bool classof(const Instruction *B)
Definition uArchBase.h:207
int32_t getMaxLaneAccessSizeBytes() const override
Definition uArchBase.h:221
std::optional< BlockShapes > computeBlockWidthHeightCount(Type elemTy, bool hasTransform, bool hasTranspose, bool upConv) const override
Definition uArchBase.h:277
static bool classof(const Instruction *B)
Definition uArchBase.h:267
static bool classof(const Instruction *B)
Definition uArchBase.h:334
std::optional< BlockShapes > computeBlockWidthHeightCount(Type elemTy, bool, bool, bool) const override
Definition uArchBase.h:344
static bool classof(const Instruction *B)
Definition uArchBase.h:237
std::optional< BlockShapes > computeBlockWidthHeightCount(Type elemTy, bool, bool, bool) const override
Definition uArchBase.h:247
llvm::SmallVector< uint32_t, 8 > getSupportedK(Type type) const override
Definition uArchBase.h:497
bool isLaneLayoutRowMajorOrder() const override
Definition uArchBase.h:398
llvm::SmallVector< std::pair< uint32_t, uint32_t >, 16 > getSupportedShapes(Type dataType, MMAOpndKind matrixType) override
Definition uArchBase.h:455
llvm::SmallVector< Type, 8 > getSupportedTypes(MLIRContext &context, MMAOpndKind matrixType) override
Definition uArchBase.h:473
llvm::SmallVector< uint32_t, 8 > getSupportedN(Type type) const override
Definition uArchBase.h:521
SubgroupMatrixMultiplyAcc(unsigned packedFormatBitSizeA, unsigned packedFormatBitSizeB)
Definition uArchBase.h:374
static bool classof(const Instruction *B)
Definition uArchBase.h:380
llvm::SmallVector< uint32_t, 8 > getSupportedM(Type type) const override
Definition uArchBase.h:492
llvm::SmallVector< uint32_t, 8 > getSupportedN(Type type) const override
Definition uArchBase.h:585
llvm::SmallVector< uint32_t, 8 > getSupportedM(Type type) const override
Definition uArchBase.h:563
SubgroupScaledMatrixMultiplyAcc(unsigned packedFormatBitSizeA, unsigned packedFormatBitSizeB)
Definition uArchBase.h:407
static bool classof(const Instruction *B)
Definition uArchBase.h:413
llvm::SmallVector< std::pair< uint32_t, uint32_t >, 16 > getSupportedShapes(Type dataType, MMAOpndKind matrixType) override
Definition uArchBase.h:526
llvm::SmallVector< Type, 8 > getSupportedTypes(MLIRContext &context, MMAOpndKind matrixType) override
Definition uArchBase.h:543
llvm::SmallVector< uint32_t, 8 > getSupportedK(Type type) const override
Definition uArchBase.h:568
llvm::SmallDenseMap< InstructionKind, const Instruction *, 32 > instructionRegistry
Definition uArchBase.h:129
virtual unsigned getGeneralPackedFormatBitSize() const =0
bool isSupportedInstruction(InstructionKind instr) const
Definition uArchBase.h:122
virtual int getSubgroupSize() const =0
const Instruction * getInstruction(InstructionKind instKind) const
Definition uArchBase.h:115
virtual ~uArch()=default
uArch(Kind kind, llvm::ArrayRef< const Instruction * > instructionRegistry)
Definition uArchBase.h:104