MLIR 24.0.0git
X86Dialect.cpp
Go to the documentation of this file.
1//===- X86Dialect.cpp - MLIR X86 ops implementation -----------------------===//
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// This file implements the X86 dialect and its operations.
10//
11//===----------------------------------------------------------------------===//
12
17#include "mlir/IR/Builders.h"
20
21using namespace mlir;
22
23#include "mlir/Dialect/X86/X86Interfaces.cpp.inc"
24
25#include "mlir/Dialect/X86/X86Dialect.cpp.inc"
26
27void x86::X86Dialect::initialize() {
28 addTypes<
29#define GET_TYPEDEF_LIST
30#include "mlir/Dialect/X86/X86Types.cpp.inc"
31 >();
32
33 addOperations<
34#define GET_OP_LIST
35#include "mlir/Dialect/X86/X86.cpp.inc"
36 >();
37}
38
39static Value getMemrefBuffPtr(Location loc, MemRefType type, Value buffer,
40 const LLVMTypeConverter &typeConverter,
41 RewriterBase &rewriter) {
42 MemRefDescriptor memRefDescriptor(buffer);
43 return memRefDescriptor.bufferPtr(rewriter, loc, typeConverter, type);
44}
45
46LogicalResult x86::avx512::MaskCompressOp::verify() {
47 if (getSrc() && getConstantSrc())
48 return emitError("cannot use both src and constant_src");
49
50 if (getSrc() && (getSrc().getType() != getDst().getType()))
51 return emitError("failed to verify that src and dst have same type");
52
53 if (getConstantSrc() && (getConstantSrc()->getType() != getDst().getType()))
54 return emitError(
55 "failed to verify that constant_src and dst have same type");
56
57 return success();
58}
59
60SmallVector<Value> x86::avx512::MaskCompressOp::getIntrinsicOperands(
61 ArrayRef<Value> operands, const LLVMTypeConverter &typeConverter,
62 RewriterBase &rewriter) {
63 auto loc = getLoc();
64 Adaptor adaptor(operands, *this);
65
66 auto opType = adaptor.getA().getType();
67 Value src;
68 if (adaptor.getSrc()) {
69 src = adaptor.getSrc();
70 } else if (adaptor.getConstantSrc()) {
71 src = LLVM::ConstantOp::create(rewriter, loc, opType,
72 adaptor.getConstantSrcAttr());
73 } else {
74 auto zeroAttr = rewriter.getZeroAttr(opType);
75 src = LLVM::ConstantOp::create(rewriter, loc, opType, zeroAttr);
76 }
77
78 return SmallVector<Value>{adaptor.getA(), src, adaptor.getK()};
79}
80
82x86::avx::DotOp::getIntrinsicOperands(ArrayRef<Value> operands,
83 const LLVMTypeConverter &typeConverter,
84 RewriterBase &rewriter) {
85 SmallVector<Value> intrinsicOperands(operands);
86 // Dot product of all elements, broadcasted to all elements.
87 Value scale =
88 LLVM::ConstantOp::create(rewriter, getLoc(), rewriter.getI8Type(), 0xff);
89 intrinsicOperands.push_back(scale);
90
91 return intrinsicOperands;
92}
93
94SmallVector<Value> x86::avx::BcstToPackedF32Op::getIntrinsicOperands(
95 ArrayRef<Value> operands, const LLVMTypeConverter &typeConverter,
96 RewriterBase &rewriter) {
97 Adaptor adaptor(operands, *this);
98 return {getMemrefBuffPtr(getLoc(), getA().getType(), adaptor.getA(),
99 typeConverter, rewriter)};
100}
101
102SmallVector<Value> x86::avx::CvtPackedEvenIndexedToF32Op::getIntrinsicOperands(
103 ArrayRef<Value> operands, const LLVMTypeConverter &typeConverter,
104 RewriterBase &rewriter) {
105 Adaptor adaptor(operands, *this);
106 return {getMemrefBuffPtr(getLoc(), getA().getType(), adaptor.getA(),
107 typeConverter, rewriter)};
108}
109
110SmallVector<Value> x86::avx::CvtPackedOddIndexedToF32Op::getIntrinsicOperands(
111 ArrayRef<Value> operands, const LLVMTypeConverter &typeConverter,
112 RewriterBase &rewriter) {
113 Adaptor adaptor(operands, *this);
114 return {getMemrefBuffPtr(getLoc(), getA().getType(), adaptor.getA(),
115 typeConverter, rewriter)};
116}
117
118/// Verify that AMX supports the implied tile shape.
119static LogicalResult verifyTileSize(Operation *op, x86::amx::TileType tp) {
120 const unsigned kMaxRows = 16;
121 const unsigned kBitsPerRow = 64 * 8;
122 unsigned col = tp.getDimSize(1) * tp.getElementType().getIntOrFloatBitWidth();
123 if (tp.getDimSize(0) > kMaxRows)
124 return op->emitOpError("bad row height: ") << tp.getDimSize(0);
125 if (col > kBitsPerRow || col & 0x1f)
126 return op->emitOpError("bad column width: ") << (col >> 3);
127 return success();
128}
129
130/// Verify that AMX supports the multiplication.
131static LogicalResult verifyMultShape(Operation *op, x86::amx::TileType atp,
133 x86::amx::TileType ctp, unsigned scale) {
134 unsigned am = atp.getDimSize(0), ak = atp.getDimSize(1) >> scale;
135 unsigned bk = btp.getDimSize(0), bn = btp.getDimSize(1) >> scale;
136 unsigned cm = ctp.getDimSize(0), cn = ctp.getDimSize(1);
137 if (cm != am || cn != bn || ak != bk)
138 return op->emitOpError("bad mult shape: ")
139 << cm << " x " << cn << " x " << ak;
140 return success();
141}
142
143/// Maps the 2-dim vector shape to the two 16-bit tile sizes. The first
144/// dimension directly translates into the number of rows of the tiles.
145/// The second dimensions needs to be scaled by the number of bytes.
147 RewriterBase &rewriter) {
148 Type llvmInt16Type = rewriter.getIntegerType(16);
149 unsigned width = tType.getElementType().getIntOrFloatBitWidth();
150 assert(llvm::isPowerOf2_64(width) && width >= 8);
151 unsigned bytes = width >> 3;
152 auto mattr = rewriter.getI16IntegerAttr(tType.getDimSize(0));
153 auto nattr = rewriter.getI16IntegerAttr(tType.getDimSize(1) * bytes);
154 return SmallVector<Value>{
155 LLVM::ConstantOp::create(rewriter, loc, llvmInt16Type, mattr),
156 LLVM::ConstantOp::create(rewriter, loc, llvmInt16Type, nattr)};
157}
158
159/// Returns stride expressed in number of bytes for the given `elementStride`
160/// stride encoded in number of elements of the type `mType`.
161static Value computeStrideInBytes(Location loc, MemRefType mType,
162 Value elementStride, RewriterBase &rewriter) {
163 Type llvmInt64Type = rewriter.getIntegerType(64);
164 unsigned bytes = mType.getElementType().getIntOrFloatBitWidth() / 8;
165 auto attr = rewriter.getI64IntegerAttr(bytes);
166 Value scale = LLVM::ConstantOp::create(rewriter, loc, llvmInt64Type, attr);
167 return LLVM::MulOp::create(rewriter, loc, llvmInt64Type, scale, elementStride)
168 .getResult();
169}
170
171/// Maps the 2-dim memref shape to the 64-bit stride. Note that the buffer
172/// shape may "envelop" the actual tile shape, and may be dynamically sized.
173static Value inferStride(Location loc, MemRefType mType, Value base,
174 RewriterBase &rewriter) {
175 assert(mType.getRank() >= 2 && "Invalid shape for AMX strides");
176 int64_t preLast = mType.getRank() - 2;
177 Type llvmInt64Type = rewriter.getIntegerType(64);
178 unsigned width = mType.getElementType().getIntOrFloatBitWidth();
179 assert(llvm::isPowerOf2_64(width) && width >= 8);
180 unsigned bytes = width >> 3;
181 auto [strides, offset] = mType.getStridesAndOffset();
182 if (strides[preLast] == ShapedType::kDynamic) {
183 // Dynamic stride needs code to compute the stride at runtime.
184 MemRefDescriptor memrefDescriptor(base);
186 loc, mType, memrefDescriptor.stride(rewriter, loc, preLast), rewriter);
187 }
188 // Use direct constant for static stride.
189 auto attr = rewriter.getI64IntegerAttr(strides[preLast] * bytes);
190 return LLVM::ConstantOp::create(rewriter, loc, llvmInt64Type, attr)
191 .getResult();
192}
193
194LogicalResult x86::amx::TileZeroOp::verify() {
195 return verifyTileSize(*this, getTileType());
196}
197
198SmallVector<Value> x86::amx::TileZeroOp::getIntrinsicOperands(
199 ArrayRef<Value> operands, const LLVMTypeConverter &typeConverter,
200 RewriterBase &rewriter) {
201 return getTileSizes(getLoc(), getTileType(), rewriter);
202}
203
204template <typename OpTy, typename = std::enable_if_t<
205 std::is_same_v<OpTy, x86::amx::TileLoadOp> ||
206 std::is_same_v<OpTy, x86::amx::TileStoreOp>>>
207static LogicalResult tileTransferVerifier(OpTy op) {
208 MemRefType memrefTy = op.getMemRefType();
209 unsigned rank = memrefTy.getRank();
210 if (op.getIndices().size() != rank)
211 return op.emitOpError("requires ") << rank << " indices";
212
213 if (failed(verifyTileSize(op, op.getTileType())))
214 return failure();
215
216 // Validate basic buffer properties when the stride is implicit.
217 if (!op.getStride()) {
218 if (rank < 2)
219 return op.emitOpError("requires at least 2D memref");
220 SmallVector<int64_t> strides;
221 int64_t offset;
222 if (failed(memrefTy.getStridesAndOffset(strides, offset)) ||
223 strides.back() != 1)
224 return op.emitOpError("requires memref with unit innermost stride");
225 }
226
227 return success();
228}
229
230void x86::amx::TileLoadOp::build(OpBuilder &builder, OperationState &state,
231 Type res, Value base, ValueRange indices) {
232 build(builder, state, res, base, indices, /*stride=*/nullptr);
233}
234
235LogicalResult x86::amx::TileLoadOp::verify() {
236 return tileTransferVerifier(*this);
237}
238
239SmallVector<Value> x86::amx::TileLoadOp::getIntrinsicOperands(
240 ArrayRef<Value> operands, const LLVMTypeConverter &typeConverter,
241 RewriterBase &rewriter) {
242 auto loc = getLoc();
243 Adaptor adaptor(operands, *this);
244
245 SmallVector<Value> intrinsicOperands;
246 intrinsicOperands.append(getTileSizes(loc, getTileType(), rewriter));
247 intrinsicOperands.push_back(
248 LLVM::getStridedElementPtr(rewriter, loc, typeConverter, getMemRefType(),
249 adaptor.getBase(), adaptor.getIndices()));
250 if (Value stride = adaptor.getStride())
251 intrinsicOperands.push_back(
252 computeStrideInBytes(loc, getMemRefType(), stride, rewriter));
253 else
254 intrinsicOperands.push_back(
255 inferStride(loc, getMemRefType(), adaptor.getBase(), rewriter));
256
257 return intrinsicOperands;
258}
259
260void x86::amx::TileStoreOp::build(OpBuilder &builder, OperationState &state,
261 Value base, ValueRange indices, Value val) {
262 build(builder, state, base, indices, val, /*stride=*/nullptr);
263}
264
265LogicalResult x86::amx::TileStoreOp::verify() {
266 return tileTransferVerifier(*this);
267}
268
269SmallVector<Value> x86::amx::TileStoreOp::getIntrinsicOperands(
270 ArrayRef<Value> operands, const LLVMTypeConverter &typeConverter,
271 RewriterBase &rewriter) {
272 auto loc = getLoc();
273 Adaptor adaptor(operands, *this);
274
275 SmallVector<Value> intrinsicOperands;
276 intrinsicOperands.append(getTileSizes(loc, getTileType(), rewriter));
277 intrinsicOperands.push_back(
278 LLVM::getStridedElementPtr(rewriter, loc, typeConverter, getMemRefType(),
279 adaptor.getBase(), adaptor.getIndices()));
280 if (Value stride = adaptor.getStride())
281 intrinsicOperands.push_back(
282 computeStrideInBytes(loc, getMemRefType(), stride, rewriter));
283 else
284 intrinsicOperands.push_back(
285 inferStride(loc, getMemRefType(), adaptor.getBase(), rewriter));
286 intrinsicOperands.push_back(adaptor.getVal());
287
288 return intrinsicOperands;
289}
290
291LogicalResult x86::amx::TileMulFOp::verify() {
292 x86::amx::TileType aType = getLhsTileType();
293 x86::amx::TileType bType = getRhsTileType();
294 x86::amx::TileType cType = getTileType();
295 unsigned scale = 1;
296 if (aType.getElementType().isF8E4M3FN() || aType.getElementType().isF8E5M2())
297 scale = 2;
298 if (failed(verifyTileSize(*this, aType)) ||
299 failed(verifyTileSize(*this, bType)) ||
300 failed(verifyTileSize(*this, cType)) ||
301 failed(verifyMultShape(*this, aType, bType, cType, scale)))
302 return failure();
303 Type ta = aType.getElementType();
304 Type tb = bType.getElementType();
305 Type tc = cType.getElementType();
306 bool flag1 = !ta.isBF16() && !ta.isF16() &&
307 !((ta.isF8E4M3FN() || ta.isF8E5M2()) &&
308 (tb.isF8E4M3FN() || tb.isF8E5M2()));
309 bool flag2 = (ta.isBF16() || ta.isF16()) ? (ta != tb) : false;
310 if (flag1 || flag2 || !tc.isF32())
311 return emitOpError("unsupported type combination");
312 return success();
313}
314
315SmallVector<Value> x86::amx::TileMulFOp::getIntrinsicOperands(
316 ArrayRef<Value> operands, const LLVMTypeConverter &typeConverter,
317 RewriterBase &rewriter) {
318 auto loc = getLoc();
319 Adaptor adaptor(operands, *this);
320
321 x86::amx::TileType aType = getLhsTileType();
322 x86::amx::TileType bType = getRhsTileType();
323 SmallVector<Value> tsza = getTileSizes(loc, aType, rewriter);
324 SmallVector<Value> tszb = getTileSizes(loc, bType, rewriter);
325
326 SmallVector<Value> intrinsicOperands = {tsza[0], tszb[1],
327 tsza[1], adaptor.getAcc(),
328 adaptor.getLhs(), adaptor.getRhs()};
329
330 return intrinsicOperands;
331}
332
333LogicalResult x86::amx::TileMulIOp::verify() {
334 x86::amx::TileType aType = getLhsTileType();
335 x86::amx::TileType bType = getRhsTileType();
336 x86::amx::TileType cType = getTileType();
337 if (failed(verifyTileSize(*this, aType)) ||
338 failed(verifyTileSize(*this, bType)) ||
339 failed(verifyTileSize(*this, cType)) ||
340 failed(verifyMultShape(*this, aType, bType, cType, 2)))
341 return failure();
342 Type ta = aType.getElementType();
343 Type tb = bType.getElementType();
344 Type tc = cType.getElementType();
345 if (!ta.isInteger(8) || !tb.isInteger(8) || !tc.isInteger(32))
346 return emitOpError("unsupported type combination");
347 return success();
348}
349
350SmallVector<Value> x86::amx::TileMulIOp::getIntrinsicOperands(
351 ArrayRef<Value> operands, const LLVMTypeConverter &typeConverter,
352 RewriterBase &rewriter) {
353 auto loc = getLoc();
354 Adaptor adaptor(operands, *this);
355
356 x86::amx::TileType aType = getLhsTileType();
357 x86::amx::TileType bType = getRhsTileType();
358 SmallVector<Value> tsza = getTileSizes(loc, aType, rewriter);
359 SmallVector<Value> tszb = getTileSizes(loc, bType, rewriter);
360
361 SmallVector<Value> intrinsicOperands = {tsza[0], tszb[1],
362 tsza[1], adaptor.getAcc(),
363 adaptor.getLhs(), adaptor.getRhs()};
364
365 return intrinsicOperands;
366}
367
368Type x86::amx::TileType::parse(AsmParser &parser) {
369 if (parser.parseLess())
370 return nullptr;
371
373 if (parser.parseDimensionList(shape, false, true))
374 return nullptr;
375
376 Type elementType;
377 if (parser.parseType(elementType))
378 return nullptr;
379
380 if (parser.parseGreater())
381 return nullptr;
382
383 return AMXTileType::getChecked(
384 [&] { return parser.emitError(parser.getNameLoc()); }, shape,
385 elementType);
386}
387
388void x86::amx::TileType::print(AsmPrinter &os) const {
389 os << "<";
391 os << 'x';
393 os << '>';
394}
395
396#define GET_OP_CLASSES
397#include "mlir/Dialect/X86/X86.cpp.inc"
398
399#define GET_TYPEDEF_CLASSES
400#include "mlir/Dialect/X86/X86Types.cpp.inc"
return success()
false
Parses a map_entries map type from a string format back into its numeric value.
static Type getElementType(Type type, ArrayRef< int32_t > indices, function_ref< InFlightDiagnostic(StringRef)> emitErrorFn)
Walks the given type hierarchy with the given indices, potentially down to component granularity,...
Definition SPIRVOps.cpp:229
static ArrayRef< int64_t > getShape(Type type)
Returns the shape of the given type.
Definition Traits.cpp:117
static Value getMemrefBuffPtr(Location loc, MemRefType type, Value buffer, const LLVMTypeConverter &typeConverter, RewriterBase &rewriter)
static Value inferStride(Location loc, MemRefType mType, Value base, RewriterBase &rewriter)
Maps the 2-dim memref shape to the 64-bit stride.
static Value computeStrideInBytes(Location loc, MemRefType mType, Value elementStride, RewriterBase &rewriter)
Returns stride expressed in number of bytes for the given elementStride stride encoded in number of e...
static LogicalResult tileTransferVerifier(OpTy op)
static LogicalResult verifyMultShape(Operation *op, x86::amx::TileType atp, x86::amx::TileType btp, x86::amx::TileType ctp, unsigned scale)
Verify that AMX supports the multiplication.
static SmallVector< Value > getTileSizes(Location loc, x86::amx::TileType tType, RewriterBase &rewriter)
Maps the 2-dim vector shape to the two 16-bit tile sizes.
static LogicalResult verifyTileSize(Operation *op, x86::amx::TileType tp)
Verify that AMX supports the implied tile shape.
This base class exposes generic asm parser hooks, usable across the various derived parsers.
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
virtual ParseResult parseLess()=0
Parse a '<' token.
virtual ParseResult parseDimensionList(SmallVectorImpl< int64_t > &dimensions, bool allowDynamic=true, bool withTrailingX=true)=0
Parse a dimension list of a tensor or memref type.
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseGreater()=0
Parse a '>' token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
This base class exposes generic asm printer hooks, usable across the various derived printers.
virtual void printType(Type type)
void printDimensionList(ArrayRef< int64_t > shape)
IntegerAttr getI16IntegerAttr(int16_t value)
Definition Builders.cpp:225
IntegerAttr getI64IntegerAttr(int64_t value)
Definition Builders.cpp:120
IntegerType getIntegerType(unsigned width)
Definition Builders.cpp:75
TypedAttr getZeroAttr(Type type)
Definition Builders.cpp:333
IntegerType getI8Type()
Definition Builders.cpp:67
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
Helper class to produce LLVM dialect operations extracting or inserting elements of a MemRef descript...
Value bufferPtr(OpBuilder &builder, Location loc, const LLVMTypeConverter &converter, MemRefType type)
Builds IR for getting the start address of the buffer represented by this memref: memref....
Value stride(OpBuilder &builder, Location loc, unsigned pos)
Builds IR extracting the pos-th size from the descriptor.
This class helps build Operations.
Definition Builders.h:210
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isF8E5M2() const
Definition Types.cpp:45
bool isF8E4M3FN() const
Definition Types.cpp:44
bool isF32() const
Definition Types.cpp:40
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
bool isBF16() const
Definition Types.cpp:37
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:392
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
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
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:744
MemRefType getMemRefType(T &&t)
Convenience method to abbreviate casting getType().
mlir::x86::AMXTileType TileType
Definition X86Dialect.h:36
Include the generated interface declarations.
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.
This represents an operation in an abstracted form, suitable for use with the builder APIs.