MLIR-AIE 1.4.5.dev
AIEVecAIE1Ops.cpp
Go to the documentation of this file.
1//===-- AIEVecAIE1Ops.cpp - MLIR AIE Vector Dialect Operations --*- C++ -*-===//
2//
3// Copyright (C) 2024 Advanced Micro Devices, Inc.
4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5//
6//===----------------------------------------------------------------------===//
7// This file implements AIE1 vector op printing, pasing, and verification.
8//===----------------------------------------------------------------------===//
9
10#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
11#include "mlir/IR/DialectImplementation.h"
12#include "mlir/IR/OpDefinition.h"
13#include "mlir/IR/TypeUtilities.h"
14#include "mlir/Transforms/FoldUtils.h"
15#include "llvm/ADT/TypeSwitch.h"
16
19
20using namespace llvm;
21using namespace mlir;
22
23#include "aie/Dialect/AIEVec/AIE1/IR/AIEVecAIE1OpsDialect.cpp.inc"
24
25namespace xilinx::aievec::aie1 {
26
27//===----------------------------------------------------------------------===//
28// AIEVecAIE1Dialect
29//===----------------------------------------------------------------------===//
30
31void AIEVecAIE1Dialect::initialize() {
32 addOperations<
33#define GET_OP_LIST
34#include "aie/Dialect/AIEVec/AIE1/IR/AIEVecAIE1Ops.cpp.inc"
35 >();
36}
37
38//===----------------------------------------------------------------------===//
39// AddOp and SubOp
40//===----------------------------------------------------------------------===//
41
42// Print out Add and Sub op.
43template <typename T>
44static void printAddSubOp(OpAsmPrinter &p, T op) {
45 // Print the lhs operand
46 p << " " << op.getLhs();
47 // Print the rhs operand
48 p << ", " << op.getRhs();
49
50 // Print the attributes, but don't print attributes that are empty strings
51 SmallVector<StringRef, 10> elidedAttrs;
52 for (int idx = 0; idx < 2; ++idx) {
53 if (op.getStart(idx).empty())
54 elidedAttrs.push_back(op.getStartAttrName(idx));
55 if (op.getOffset(idx).empty())
56 elidedAttrs.push_back(op.getOffsetAttrName(idx));
57 if (op.getOffsetHi(idx).empty())
58 elidedAttrs.push_back(op.getOffsetHiAttrName(idx));
59 if (op.getSquare(idx).empty())
60 elidedAttrs.push_back(op.getSquareAttrName(idx));
61 }
62 p.printOptionalAttrDict(op->getAttrs(), elidedAttrs);
63
64 // And now print the types
65 p << " : " << op.getLhs().getType() << ", " << op.getRhs().getType();
66 p << ", " << op.getResult().getType();
67}
68
69void AddOp::print(OpAsmPrinter &p) { printAddSubOp<AddOp>(p, *this); }
70
71void SubOp::print(OpAsmPrinter &p) { printAddSubOp<SubOp>(p, *this); }
72
73// Verify Add and Sub op.
74template <typename T>
75static LogicalResult verifyAddSubOp(T op) {
76 // Verify the types
77 auto resultType = llvm::dyn_cast<VectorType>(op.getResult().getType());
78 auto lhsType = llvm::dyn_cast<VectorType>(op.getLhs().getType());
79 auto rhsType = llvm::dyn_cast<VectorType>(op.getRhs().getType());
80
81 if (!lhsType || !rhsType || !resultType)
82 return op.emitError("requires vector type");
83
84 // All the vector types must match
85 if (lhsType != rhsType || rhsType != resultType)
86 return op.emitError("all vectors must be of same type");
87
88 return success();
89}
90
91LogicalResult AddOp::verify() { return verifyAddSubOp<AddOp>(*this); }
92
93LogicalResult SubOp::verify() { return verifyAddSubOp<SubOp>(*this); }
94
95// Parse Add and Sub op.
96static ParseResult parseAddSubOp(OpAsmParser &parser, OperationState &result) {
97 llvm::SMLoc typesLoc;
98 SmallVector<Type, 3> types;
99 OpAsmParser::UnresolvedOperand lhs, rhs;
100
101 // Parse the lhs and rhs
102 if (parser.parseOperand(lhs) || parser.parseComma() ||
103 parser.parseOperand(rhs))
104 return failure();
105
106 // Parse all the attributes and types
107 if (parser.parseOptionalAttrDict(result.attributes) ||
108 parser.getCurrentLocation(&typesLoc) || parser.parseColonTypeList(types))
109 return failure();
110
111 // Assert that there are three types: lhs, rhs, and result
112 if (types.size() != 3)
113 return parser.emitError(typesLoc, "requires three types");
114
115 // Some verification
116 VectorType lhsType = llvm::dyn_cast<VectorType>(types[0]);
117 if (!lhsType)
118 return parser.emitError(typesLoc, "requires vector type");
119 VectorType rhsType = llvm::dyn_cast<VectorType>(types[1]);
120 if (!rhsType)
121 return parser.emitError(typesLoc, "requires vector type");
122 VectorType resultType = llvm::dyn_cast<VectorType>(types[2]);
123 if (!resultType)
124 return parser.emitError(typesLoc, "requires vector type");
125
126 // Populate the lhs, rhs, and accumulator in the result
127 if (parser.resolveOperand(lhs, lhsType, result.operands) ||
128 parser.resolveOperand(rhs, rhsType, result.operands))
129 return failure();
130
131 return parser.addTypeToList(resultType, result.types);
132}
133
134ParseResult AddOp::parse(OpAsmParser &parser, OperationState &result) {
135 return parseAddSubOp(parser, result);
136}
137
138ParseResult SubOp::parse(OpAsmParser &parser, OperationState &result) {
139 return parseAddSubOp(parser, result);
140}
141
142//===----------------------------------------------------------------------===//
143// MulOp and FMAOp
144//===----------------------------------------------------------------------===//
145
146// MulOp and FMAOp are structurally similar, except that FMA op has few extra
147// fields (accumulator, bool flag to indicate if it is fmsub, etc.). We create
148// some specializations to print those fields specifically for FMA op.
149
150// Print the accumulator
151template <typename T>
152void printAccumulator(OpAsmPrinter &p, T op);
153template <>
154inline void printAccumulator(OpAsmPrinter &p, FMAOp op) {
155 p << ", " << op.getAcc();
156}
157template <>
158inline void printAccumulator(OpAsmPrinter &p, MulOp op) {}
159
160// Mark fmsub indicator as elided if the FMA op is not fmsub
161template <typename T>
162void elideFMSubAttr(T op, SmallVector<StringRef, 10> &elidedAttrs);
163template <>
164inline void elideFMSubAttr(FMAOp op, SmallVector<StringRef, 10> &elidedAttrs) {
165 if (!op.getFmsub())
166 elidedAttrs.push_back(op.getSubAttrName());
167}
168template <>
169inline void elideFMSubAttr(MulOp, SmallVector<StringRef, 10> &elidedAttrs) {}
170
171// Print out Mul and FMA op.
172template <typename T>
173static void printMulFMAOp(OpAsmPrinter &p, T op) {
174 // Print the left operand
175 p << " " << op.getLhs();
176 // Print the right operand
177 p << ", " << op.getRhs();
178 // For fma op, print the accumulator
179 printAccumulator(p, op);
180
181 // Print the attributes, but don't print attributes that are empty strings
182 SmallVector<StringRef, 10> elidedAttrs;
183 for (int idx = 0; idx < 2; ++idx) {
184 if (op.getStart(idx).empty())
185 elidedAttrs.push_back(op.getStartAttrName(idx));
186 if (op.getOffset(idx).empty())
187 elidedAttrs.push_back(op.getOffsetAttrName(idx));
188 if (op.getOffsetHi(idx).empty())
189 elidedAttrs.push_back(op.getOffsetHiAttrName(idx));
190 if (op.getStep(idx).empty())
191 elidedAttrs.push_back(op.getStepAttrName(idx));
192 if (op.getSquare(idx).empty())
193 elidedAttrs.push_back(op.getSquareAttrName(idx));
194 elideFMSubAttr(op, elidedAttrs);
195 }
196 p.printOptionalAttrDict(op->getAttrs(), elidedAttrs);
197
198 // And now print the types
199 p << " : " << op.getLhs().getType() << ", " << op.getRhs().getType();
200 p << ", " << op.getResult().getType();
201}
202
203void MulOp::print(OpAsmPrinter &p) { printMulFMAOp<MulOp>(p, *this); }
204
205void FMAOp::print(OpAsmPrinter &p) { printMulFMAOp<FMAOp>(p, *this); }
206
207// Verify Mul and FMA op.
208template <typename T>
209static LogicalResult verifyMulFMAOp(T op) {
210 // Verify the types
211 auto lhsType = llvm::dyn_cast<VectorType>(op.getLhs().getType());
212 auto rhsType = llvm::dyn_cast<VectorType>(op.getRhs().getType());
213
214 if (!lhsType || !rhsType)
215 return op.emitError("requires vector type");
216
217 auto resultType = llvm::dyn_cast<VectorType>(op.getResult().getType());
218 if (!resultType)
219 return op.emitError("requires vector type");
220
221 // Additional checks for FMA op
222 // Get the width of the underlying scalars of all the vectors
223 Type ltype = lhsType.getElementType();
224 Type rtype = rhsType.getElementType();
225 Type atype = resultType.getElementType();
226 unsigned ltypeWidth = ltype.getIntOrFloatBitWidth();
227 unsigned rtypeWidth = rtype.getIntOrFloatBitWidth();
228 unsigned atypeWidth = atype.getIntOrFloatBitWidth();
229
230 // Checks on the number of lanes
231 unsigned accLanes = getVectorLaneSize(resultType);
232 unsigned rhsLanes = getVectorLaneSize(rhsType);
233 unsigned lhsLanes = getVectorLaneSize(lhsType);
234
235 // If this is not a simple scheme, perform complex checks
236 if (accLanes != rhsLanes || accLanes != lhsLanes) {
237 if (rhsLanes != 256 / rtypeWidth)
238 return op.emitError("incorrect rhs operand vector lanes");
239 if (lhsLanes < 2 * rhsLanes)
240 return op.emitError("The number of lanes in lhs operand "
241 "must be at least twice that of rhs operand");
242 if (accLanes > rhsLanes)
243 return op.emitError("The number of lanes in accumulator "
244 "must be less than that of rhs operand");
245 }
246
247 // lhs and rhs vector's element type must match
248 if (ltype != rtype)
249 return op.emitError("The element type of lhs and rhs "
250 "operand vectors must match");
251
252 // The datatype of accumulator must always be greater width
253 if (isa<IntegerType>(atype)) {
254 if (!isa<IntegerType>(ltype))
255 return op.emitError("Integer result must have integer operands");
256
257 if (ltypeWidth >= atypeWidth || rtypeWidth >= atypeWidth)
258 return op.emitError("the element type of accumulator must have "
259 "wider width than that of the operand vectors");
260 } else if (isa<FloatType>(atype)) {
261 if (!isa<FloatType>(ltype))
262 return op.emitError("Floating point result must have "
263 "floating point operands");
264
265 if (ltypeWidth != atypeWidth || rtypeWidth != atypeWidth)
266 return op.emitError("the element type of accumulator must be "
267 "same width as the operand vectors");
268 }
269
270 return success();
271}
272
273LogicalResult MulOp::verify() { return verifyMulFMAOp<MulOp>(*this); }
274
275LogicalResult FMAOp::verify() { return verifyMulFMAOp<FMAOp>(*this); }
276
277// Parse Mul and FMA op.
278static ParseResult parseMulFMAOp(OpAsmParser &parser, OperationState &result,
279 bool isFMAOp = true) {
280 llvm::SMLoc typesLoc;
281 SmallVector<Type, 3> types;
282 OpAsmParser::UnresolvedOperand lhs, rhs, acc;
283
284 // Parse the lhs and rhs
285 if (parser.parseOperand(lhs) || parser.parseComma() ||
286 parser.parseOperand(rhs))
287 return failure();
288
289 // Parse the acc for FMA op
290 if (isFMAOp) {
291 if (parser.parseComma() || parser.parseOperand(acc))
292 return failure();
293 }
294
295 // Parse all the attributes and types
296 if (parser.parseOptionalAttrDict(result.attributes) ||
297 parser.getCurrentLocation(&typesLoc) || parser.parseColonTypeList(types))
298 return failure();
299
300 // Assert that there are three types: lhs, rhs, and acc
301 if (types.size() != 3)
302 return parser.emitError(typesLoc, "requires three types");
303
304 // Some verification
305 VectorType lhsType = llvm::dyn_cast<VectorType>(types[0]);
306 if (!lhsType)
307 return parser.emitError(typesLoc, "requires vector type");
308 VectorType rhsType = llvm::dyn_cast<VectorType>(types[1]);
309 if (!rhsType)
310 return parser.emitError(typesLoc, "requires vector type");
311
312 // Int ops use the accumulator while float ops use normal vector registers
313 VectorType accType = llvm::dyn_cast<VectorType>(types[2]);
314 if (!accType)
315 return parser.emitError(typesLoc, "requires vector type");
316
317 // Populate the lhs and rhs operands, and result
318 if (parser.resolveOperand(lhs, lhsType, result.operands) ||
319 parser.resolveOperand(rhs, rhsType, result.operands))
320 return failure();
321
322 // Populate acc operand for FMA op
323 if (isFMAOp) {
324 if (parser.resolveOperand(acc, accType, result.operands))
325 return failure();
326 }
327
328 return parser.addTypeToList(accType, result.types);
329}
330
331ParseResult MulOp::parse(OpAsmParser &parser, OperationState &result) {
332 return parseMulFMAOp(parser, result, false);
333}
334
335ParseResult FMAOp::parse(OpAsmParser &parser, OperationState &result) {
336 return parseMulFMAOp(parser, result, true);
337}
338
339//===----------------------------------------------------------------------===//
340// SelectOp
341//===----------------------------------------------------------------------===//
342
343// Print out select op.
344void SelectOp::print(OpAsmPrinter &p) {
345 // Print the xbuff
346 p << " " << getXbuff();
347 // Print the start, offsets, etc. for xbuff
348 if (getYbuff())
349 p << ", " << getYbuff();
350
351 // Print the attributes, but don't print attributes that are empty strings
352 SmallVector<StringRef, 10> elidedAttrs;
353 for (int idx = 0; idx < 2; ++idx) {
354 if (getStart(idx).empty())
355 elidedAttrs.push_back(getStartAttrName(idx));
356 if (getOffset(idx).empty())
357 elidedAttrs.push_back(getOffsetAttrName(idx));
358 if (getOffsetHi(idx).empty())
359 elidedAttrs.push_back(getOffsetHiAttrName(idx));
360 if (getSquare(idx).empty())
361 elidedAttrs.push_back(getSquareAttrName(idx));
362 }
363 p.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
364
365 // And now print the types
366 p << " : " << getXbuff().getType();
367 if (getYbuff())
368 p << ", " << getYbuff().getType();
369 p << ", " << getResult().getType();
370}
371
372// Verify select op.
373LogicalResult SelectOp::verify() {
374 // Verify the types
375 VectorType resultType = llvm::dyn_cast<VectorType>(getResult().getType());
376 VectorType xbuffType = llvm::dyn_cast<VectorType>(getXbuff().getType());
377
378 if (!resultType || !xbuffType)
379 return emitError("requires vector type");
380
381 // The underlying scalar element type of all vectors must match
382 Type rtype = resultType.getElementType();
383 Type xtype = xbuffType.getElementType();
384 if (rtype != xtype)
385 return emitError("types of result and xbuff must match");
386
387 // If yuff is present, its vector type should be same as xbuff
388 if (getYbuff()) {
389 VectorType ybuffType = llvm::dyn_cast<VectorType>(getYbuff().getType());
390 if (xbuffType != ybuffType)
391 return emitError("types of xbuff and ybuff must match");
392 }
393
394 // Compare the lanes. xtype should have more lanes
395 unsigned sourceLanes = getVectorLaneSize(xbuffType);
396 unsigned resultLanes = getVectorLaneSize(resultType);
397 if (sourceLanes < resultLanes)
398 return emitError("xbuff cannot be smaller than result");
399
400 return success();
401}
402
403// Parse select op.
404ParseResult SelectOp::parse(OpAsmParser &parser, OperationState &result) {
405 llvm::SMLoc typesLoc;
406 SmallVector<Type, 3> types;
407 OpAsmParser::UnresolvedOperand xbuff, ybuff;
408
409 // Parse xbuff
410 if (parser.parseOperand(xbuff))
411 return failure();
412
413 // Parse optional ybuff
414 ParseResult hasYbuff = parser.parseOptionalComma();
415 if (hasYbuff.succeeded() && parser.parseOperand(ybuff))
416 return failure();
417
418 // Parse all the attributes and types
419 if (parser.parseOptionalAttrDict(result.attributes) ||
420 parser.getCurrentLocation(&typesLoc) || parser.parseColonTypeList(types))
421 return failure();
422
423 // Assert that there is at least two types
424 if (types.size() < 2)
425 return parser.emitError(typesLoc, "requires at least two type");
426
427 // Some verification
428 VectorType xbuffType = llvm::dyn_cast<VectorType>(types[0]);
429 if (!xbuffType)
430 return parser.emitError(typesLoc, "requires vector type");
431 VectorType ybuffType;
432 if (hasYbuff.succeeded()) {
433 ybuffType = llvm::dyn_cast<VectorType>(types[1]);
434 if (!ybuffType)
435 return parser.emitError(typesLoc, "requires vector type");
436 }
437 VectorType resultType = llvm::dyn_cast<VectorType>(types.back());
438 if (!resultType)
439 return parser.emitError(typesLoc, "requires vector type");
440
441 // Populate the xbuff
442 if (parser.resolveOperand(xbuff, xbuffType, result.operands))
443 return failure();
444 // Populate optional ybuff in result
445 if (hasYbuff.succeeded())
446 if (parser.resolveOperand(ybuff, ybuffType, result.operands))
447 return failure();
448
449 return parser.addTypeToList(resultType, result.types);
450}
451
452//===----------------------------------------------------------------------===//
453// ExtOp
454//===----------------------------------------------------------------------===//
455
456// Print out Ext op.
457void ExtOp::print(OpAsmPrinter &p) {
458 // Print the source vector
459 p << " " << getSource();
460
461 // Print the attributes
462 p.printOptionalAttrDict((*this)->getAttrs());
463
464 // And now print the types
465 p << " : " << getSource().getType() << ", " << getResult().getType();
466}
467
468// Verify Ext op.
469LogicalResult ExtOp::verify() {
470 // Verify the types
471 VectorType sourceType = llvm::dyn_cast<VectorType>(getSource().getType());
472 VectorType resultType = llvm::dyn_cast<VectorType>(getResult().getType());
473 if (!sourceType || !resultType)
474 return emitError("requires vector type");
475
476 // Check the number of lanes
477 unsigned sourceLanes = getVectorLaneSize(sourceType);
478 unsigned resultLanes = getVectorLaneSize(resultType);
479 // Source lanes must be greater than result lanes
480 if (sourceLanes / resultLanes <= 1)
481 return emitError("lanes in source vector must be at least "
482 "twice that of result vector");
483 // Source lanes must be a multiple of result lanes
484 if (sourceLanes % resultLanes != 0)
485 return emitError("lanes in result vector must be a multiple "
486 "of source vector lanes");
487
488 // Verify validity of index
489 unsigned factor = sourceLanes / resultLanes;
490 if (static_cast<unsigned>(getIndex()) >= factor)
491 return emitError("index out of bounds");
492
493 // The datatype of source and result must match
494 Type stype = sourceType.getElementType();
495 Type rtype = resultType.getElementType();
496 if (stype != rtype)
497 return emitError("source and result element type must be same");
498
499 return success();
500}
501
502// Parse Ext op.
503ParseResult ExtOp::parse(OpAsmParser &parser, OperationState &result) {
504 llvm::SMLoc typesLoc;
505 SmallVector<Type, 2> types;
506 OpAsmParser::UnresolvedOperand source;
507
508 // Parse the source vector
509 if (parser.parseOperand(source))
510 return failure();
511
512 // Parse all the attributes and types
513 if (parser.parseOptionalAttrDict(result.attributes) ||
514 parser.getCurrentLocation(&typesLoc) || parser.parseColonTypeList(types))
515 return failure();
516
517 if (result.attributes.getAttrs().size() != 1)
518 return parser.emitError(typesLoc, "requires one attribute");
519
520 // Assert that there are two types (source and result)
521 if (types.size() != 2)
522 return parser.emitError(typesLoc, "requires two types");
523
524 // Some verification
525 VectorType sourceType = llvm::dyn_cast<VectorType>(types[0]);
526 VectorType resultType = llvm::dyn_cast<VectorType>(types[1]);
527 if (!sourceType || !resultType)
528 return parser.emitError(typesLoc, "requires vector type");
529
530 // Populate the source in result
531 if (parser.resolveOperand(source, sourceType, result.operands))
532 return failure();
533
534 return parser.addTypeToList(resultType, result.types);
535}
536
537} // namespace xilinx::aievec::aie1
538
539// #define GET_ATTRDEF_CLASSES
540// #include "aie/Dialect/AIEVec/IR/AIEVecAttributes.cpp.inc"
541
542#define GET_OP_CLASSES
543#include "aie/Dialect/AIEVec/AIE1/IR/AIEVecAIE1Ops.cpp.inc"
void elideFMSubAttr(T op, SmallVector< StringRef, 10 > &elidedAttrs)
void printAccumulator(OpAsmPrinter &p, T op)
unsigned getVectorLaneSize(mlir::VectorType type)
Definition AIEVecUtils.h:54