MLIR 24.0.0git
ArithToSPIRV.cpp
Go to the documentation of this file.
1//===- ArithToSPIRV.cpp - Arithmetic to SPIRV 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
21#include "llvm/ADT/APInt.h"
22#include "llvm/ADT/ArrayRef.h"
23#include "llvm/ADT/STLExtras.h"
24#include "llvm/Support/Debug.h"
25#include "llvm/Support/MathExtras.h"
26#include <cassert>
27#include <memory>
28
29namespace mlir {
30#define GEN_PASS_DEF_CONVERTARITHTOSPIRVPASS
31#include "mlir/Conversion/Passes.h.inc"
32} // namespace mlir
33
34#define DEBUG_TYPE "arith-to-spirv-pattern"
35
36using namespace mlir;
37
38//===----------------------------------------------------------------------===//
39// Conversion Helpers
40//===----------------------------------------------------------------------===//
41
42/// Converts the given `srcAttr` into a boolean attribute if it holds an
43/// integral value. Returns null attribute if conversion fails.
44static BoolAttr convertBoolAttr(Attribute srcAttr, Builder builder) {
45 if (auto boolAttr = dyn_cast<BoolAttr>(srcAttr))
46 return boolAttr;
47 if (auto intAttr = dyn_cast<IntegerAttr>(srcAttr))
48 return builder.getBoolAttr(intAttr.getValue().getBoolValue());
49 return {};
50}
51
52/// Converts the given `srcAttr` to a new attribute of the given `dstType`.
53/// Returns null attribute if conversion fails.
54static IntegerAttr convertIntegerAttr(IntegerAttr srcAttr, IntegerType dstType,
55 Builder builder) {
56 // If the source number uses less active bits than the target bitwidth, then
57 // it should be safe to convert.
58 if (srcAttr.getValue().isIntN(dstType.getWidth()))
59 return builder.getIntegerAttr(dstType, srcAttr.getInt());
60
61 // XXX: Try again by interpreting the source number as a signed value.
62 // Although integers in the standard dialect are signless, they can represent
63 // a signed number. It's the operation decides how to interpret. This is
64 // dangerous, but it seems there is no good way of handling this if we still
65 // want to change the bitwidth. Emit a message at least.
66 if (srcAttr.getValue().isSignedIntN(dstType.getWidth())) {
67 auto dstAttr = builder.getIntegerAttr(dstType, srcAttr.getInt());
68 LLVM_DEBUG(llvm::dbgs() << "attribute '" << srcAttr << "' converted to '"
69 << dstAttr << "' for type '" << dstType << "'\n");
70 return dstAttr;
71 }
72
73 LLVM_DEBUG(llvm::dbgs() << "attribute '" << srcAttr
74 << "' illegal: cannot fit into target type '"
75 << dstType << "'\n");
76 return {};
77}
78
79/// Converts the given `srcAttr` to a new attribute of the given `dstType`.
80/// Returns null attribute if `dstType` is not 32-bit or conversion fails.
81static FloatAttr convertFloatAttr(FloatAttr srcAttr, FloatType dstType,
82 Builder builder) {
83 // Only support converting to float for now.
84 if (!dstType.isF32())
85 return FloatAttr();
86
87 // Try to convert the source floating-point number to single precision.
88 APFloat dstVal = srcAttr.getValue();
89 bool losesInfo = false;
90 APFloat::opStatus status =
91 dstVal.convert(APFloat::IEEEsingle(), APFloat::rmTowardZero, &losesInfo);
92 if (status != APFloat::opOK || losesInfo) {
93 LLVM_DEBUG(llvm::dbgs()
94 << srcAttr << " illegal: cannot fit into converted type '"
95 << dstType << "'\n");
96 return FloatAttr();
97 }
98
99 return builder.getF32FloatAttr(dstVal.convertToFloat());
100}
101
102// Gets an IntegerAttr from a FloatAttr while preserving the bits. If `dstType`
103// is wider than the float (e.g. i8 emulated as i32), the bits are widened
104// like an integer constant of the float's width.
105static IntegerAttr getIntegerAttrFromFloatAttr(FloatAttr floatAttr,
106 IntegerType dstType,
107 Builder builder) {
108 APInt intVal = floatAttr.getValue().bitcastToAPInt();
109 auto srcType = IntegerType::get(dstType.getContext(), intVal.getBitWidth());
110 return convertIntegerAttr(builder.getIntegerAttr(srcType, intVal), dstType,
111 builder);
112}
113
114/// Returns true if the given `type` is a boolean scalar or vector type.
115static bool isBoolScalarOrVector(Type type) {
116 assert(type && "Not a valid type");
117 if (type.isInteger(1))
118 return true;
119
120 if (auto vecType = dyn_cast<VectorType>(type))
121 return vecType.getElementType().isInteger(1);
122
123 return false;
124}
125
126/// Creates a scalar/vector integer constant.
127static Value getScalarOrVectorConstInt(Type type, uint64_t value,
128 OpBuilder &builder, Location loc) {
129 if (auto vectorType = dyn_cast<VectorType>(type)) {
130 Attribute element = IntegerAttr::get(vectorType.getElementType(), value);
131 auto attr = SplatElementsAttr::get(vectorType, element);
132 return spirv::ConstantOp::create(builder, loc, vectorType, attr);
133 }
134
135 if (auto intType = dyn_cast<IntegerType>(type))
136 return spirv::ConstantOp::create(builder, loc, type,
137 builder.getIntegerAttr(type, value));
138
139 return nullptr;
140}
141
142/// Returns true if scalar/vector type `a` and `b` have the same number of
143/// bitwidth.
144static bool hasSameBitwidth(Type a, Type b) {
145 auto getNumBitwidth = [](Type type) {
146 unsigned bw = 0;
147 if (type.isIntOrFloat())
148 bw = type.getIntOrFloatBitWidth();
149 else if (auto vecType = dyn_cast<VectorType>(type))
150 bw = vecType.getElementTypeBitWidth() * vecType.getNumElements();
151 return bw;
152 };
153 unsigned aBW = getNumBitwidth(a);
154 unsigned bBW = getNumBitwidth(b);
155 return aBW != 0 && bBW != 0 && aBW == bBW;
156}
157
158/// Returns a source type conversion failure for `srcType` and operation `op`.
159static LogicalResult
160getTypeConversionFailure(ConversionPatternRewriter &rewriter, Operation *op,
161 Type srcType) {
162 return rewriter.notifyMatchFailure(
163 op->getLoc(),
164 llvm::formatv("failed to convert source type '{0}'", srcType));
165}
166
167/// Returns a source type conversion failure for the result type of `op`.
168static LogicalResult
169getTypeConversionFailure(ConversionPatternRewriter &rewriter, Operation *op) {
170 assert(op->getNumResults() == 1);
171 return getTypeConversionFailure(rewriter, op, op->getResultTypes().front());
172}
173
174namespace {
175
176/// Converts elementwise unary, binary and ternary arith operations to SPIR-V
177/// operations. Op can potentially support overflow flags.
178template <typename Op, typename SPIRVOp>
179struct ElementwiseArithOpPattern final : OpConversionPattern<Op> {
180 using OpConversionPattern<Op>::OpConversionPattern;
181
182 LogicalResult
183 matchAndRewrite(Op op, typename Op::Adaptor adaptor,
184 ConversionPatternRewriter &rewriter) const override {
185 assert(adaptor.getOperands().size() <= 3);
186 // Reject boolean types to allow specialized boolean patterns to handle
187 // them (e.g., addi/subi on i1 should use LogicalNotEqual, not IAdd/ISub).
188 if (!adaptor.getOperands().empty() &&
189 isBoolScalarOrVector(adaptor.getOperands().front().getType()))
190 return failure();
191 auto converter = this->template getTypeConverter<SPIRVTypeConverter>();
192 Type dstType = converter->convertType(op.getType());
193 if (!dstType) {
194 return rewriter.notifyMatchFailure(
195 op->getLoc(),
196 llvm::formatv("failed to convert type {0} for SPIR-V", op.getType()));
197 }
198
199 if (SPIRVOp::template hasTrait<OpTrait::spirv::UnsignedOp>() &&
200 !getElementTypeOrSelf(op.getType()).isIndex() &&
201 dstType != op.getType()) {
202 return op.emitError("bitwidth emulation is not implemented yet on "
203 "unsigned op pattern version");
204 }
205
206 auto overflowFlags = arith::IntegerOverflowFlags::none;
207 if (auto overflowIface =
208 dyn_cast<arith::ArithIntegerOverflowFlagsInterface>(*op)) {
209 if (converter->getTargetEnv().allows(
210 spirv::Extension::SPV_KHR_no_integer_wrap_decoration))
211 overflowFlags = overflowIface.getOverflowAttr().getValue();
212 }
213
214 auto newOp = rewriter.template replaceOpWithNewOp<SPIRVOp>(
215 op, dstType, adaptor.getOperands());
216
217 if (bitEnumContainsAny(overflowFlags, arith::IntegerOverflowFlags::nsw))
218 newOp->setDiscardableAttr(
219 getDecorationString(spirv::Decoration::NoSignedWrap),
220 rewriter.getUnitAttr());
221
222 if (bitEnumContainsAny(overflowFlags, arith::IntegerOverflowFlags::nuw))
223 newOp->setDiscardableAttr(
224 getDecorationString(spirv::Decoration::NoUnsignedWrap),
225 rewriter.getUnitAttr());
226
227 return success();
228 }
229};
230
231//===----------------------------------------------------------------------===//
232// ConstantOp
233//===----------------------------------------------------------------------===//
234
235/// Converts composite arith.constant operation to spirv.Constant.
236struct ConstantCompositeOpPattern final
237 : public OpConversionPattern<arith::ConstantOp> {
238 using Base::Base;
239
240 LogicalResult
241 matchAndRewrite(arith::ConstantOp constOp, OpAdaptor adaptor,
242 ConversionPatternRewriter &rewriter) const override {
243 auto srcType = dyn_cast<ShapedType>(constOp.getType());
244 if (!srcType || srcType.getNumElements() == 1)
245 return failure();
246
247 // arith.constant should only have vector or tensor types. This is a MLIR
248 // wide problem at the moment.
249 if (!isa<VectorType, RankedTensorType>(srcType))
250 return rewriter.notifyMatchFailure(constOp, "unsupported ShapedType");
251
252 Type dstType = getTypeConverter()->convertType(srcType);
253 if (!dstType)
254 return failure();
255
256 // Import the resource into the IR to make use of the special handling of
257 // element types later on.
258 mlir::DenseElementsAttr dstElementsAttr;
259 if (auto denseElementsAttr =
260 dyn_cast<DenseElementsAttr>(constOp.getValue())) {
261 dstElementsAttr = denseElementsAttr;
262 } else if (auto resourceAttr =
263 dyn_cast<DenseResourceElementsAttr>(constOp.getValue())) {
264
265 AsmResourceBlob *blob = resourceAttr.getRawHandle().getBlob();
266 if (!blob)
267 return constOp->emitError("could not find resource blob");
268
269 ArrayRef<char> ptr = blob->getData();
270
271 // Check that the buffer meets the requirements to get converted to a
272 // DenseElementsAttr
274 return constOp->emitError("resource is not a valid buffer");
275
276 dstElementsAttr =
277 DenseElementsAttr::getFromRawBuffer(resourceAttr.getType(), ptr);
278 } else {
279 return constOp->emitError("unsupported elements attribute");
280 }
281
282 ShapedType dstAttrType = dstElementsAttr.getType();
283
284 // If the composite type has more than one dimensions, perform
285 // linearization.
286 if (srcType.getRank() > 1) {
287 if (isa<RankedTensorType>(srcType)) {
288 dstAttrType = RankedTensorType::get(srcType.getNumElements(),
289 srcType.getElementType());
290 dstElementsAttr = dstElementsAttr.reshape(dstAttrType);
291 } else {
292 // TODO: add support for large vectors.
293 return failure();
294 }
295 }
296
297 Type srcElemType = srcType.getElementType();
298 Type dstElemType;
299 // Tensor types are converted to SPIR-V array types; vector types are
300 // converted to SPIR-V vector/array types.
301 if (auto arrayType = dyn_cast<spirv::ArrayType>(dstType))
302 dstElemType = arrayType.getElementType();
303 else
304 dstElemType = cast<VectorType>(dstType).getElementType();
305
306 // If the source and destination element types are different, perform
307 // attribute conversion.
308 if (srcElemType != dstElemType) {
310 if (isa<FloatType>(srcElemType)) {
311 for (FloatAttr srcAttr : dstElementsAttr.getValues<FloatAttr>()) {
312 Attribute dstAttr = nullptr;
313 // Handle 8-bit float conversion to 8-bit integer.
314 auto *typeConverter = getTypeConverter<SPIRVTypeConverter>();
315 if (typeConverter->getOptions().emulateUnsupportedFloatTypes &&
316 srcElemType.getIntOrFloatBitWidth() == 8 &&
317 isa<IntegerType>(dstElemType)) {
319 srcAttr, cast<IntegerType>(dstElemType), rewriter);
320 } else {
321 dstAttr = convertFloatAttr(srcAttr, cast<FloatType>(dstElemType),
322 rewriter);
323 }
324 if (!dstAttr)
325 return failure();
326 elements.push_back(dstAttr);
327 }
328 } else if (srcElemType.isInteger(1)) {
329 return failure();
330 } else {
331 for (IntegerAttr srcAttr : dstElementsAttr.getValues<IntegerAttr>()) {
332 IntegerAttr dstAttr = convertIntegerAttr(
333 srcAttr, cast<IntegerType>(dstElemType), rewriter);
334 if (!dstAttr)
335 return failure();
336 elements.push_back(dstAttr);
337 }
338 }
339
340 // Unfortunately, we cannot use dialect-specific types for element
341 // attributes; element attributes only works with builtin types. So we
342 // need to prepare another converted builtin types for the destination
343 // elements attribute.
344 if (isa<RankedTensorType>(dstAttrType))
345 dstAttrType =
346 RankedTensorType::get(dstAttrType.getShape(), dstElemType);
347 else
348 dstAttrType = VectorType::get(dstAttrType.getShape(), dstElemType);
349
350 dstElementsAttr = DenseElementsAttr::get(dstAttrType, elements);
351 }
352
353 rewriter.replaceOpWithNewOp<spirv::ConstantOp>(constOp, dstType,
354 dstElementsAttr);
355 return success();
356 }
357};
358
359/// Converts scalar arith.constant operation to spirv.Constant.
360struct ConstantScalarOpPattern final
361 : public OpConversionPattern<arith::ConstantOp> {
362 using Base::Base;
363
364 LogicalResult
365 matchAndRewrite(arith::ConstantOp constOp, OpAdaptor adaptor,
366 ConversionPatternRewriter &rewriter) const override {
367 Type srcType = constOp.getType();
368 if (auto shapedType = dyn_cast<ShapedType>(srcType)) {
369 if (shapedType.getNumElements() != 1)
370 return failure();
371 srcType = shapedType.getElementType();
372 }
373 if (!srcType.isIntOrIndexOrFloat())
374 return failure();
375
376 Attribute cstAttr = constOp.getValue();
377 if (auto elementsAttr = dyn_cast<DenseElementsAttr>(cstAttr))
378 cstAttr = elementsAttr.getSplatValue<Attribute>();
379
380 Type dstType = getTypeConverter()->convertType(srcType);
381 if (!dstType)
382 return failure();
383
384 // Floating-point types.
385 if (isa<FloatType>(srcType)) {
386 auto srcAttr = cast<FloatAttr>(cstAttr);
387 Attribute dstAttr = srcAttr;
388
389 // Floating-point types not supported in the target environment are all
390 // converted to float type.
391 auto *typeConverter = getTypeConverter<SPIRVTypeConverter>();
392 if (typeConverter->getOptions().emulateUnsupportedFloatTypes &&
393 srcType.getIntOrFloatBitWidth() == 8 && isa<IntegerType>(dstType)) {
394 // If the source is an 8-bit float, convert it to a 8-bit integer.
396 srcAttr, cast<IntegerType>(dstType), rewriter);
397 if (!dstAttr)
398 return failure();
399 } else if (srcType != dstType) {
400 dstAttr = convertFloatAttr(srcAttr, cast<FloatType>(dstType), rewriter);
401 if (!dstAttr)
402 return failure();
403 }
404
405 rewriter.replaceOpWithNewOp<spirv::ConstantOp>(constOp, dstType, dstAttr);
406 return success();
407 }
408
409 // Bool type.
410 if (srcType.isInteger(1)) {
411 // arith.constant can use 0/1 instead of true/false for i1 values. We need
412 // to handle that here.
413 auto dstAttr = convertBoolAttr(cstAttr, rewriter);
414 if (!dstAttr)
415 return failure();
416 rewriter.replaceOpWithNewOp<spirv::ConstantOp>(constOp, dstType, dstAttr);
417 return success();
418 }
419
420 // IndexType or IntegerType. Index values are converted to 32-bit integer
421 // values when converting to SPIR-V.
422 auto srcAttr = cast<IntegerAttr>(cstAttr);
423 IntegerAttr dstAttr =
424 convertIntegerAttr(srcAttr, cast<IntegerType>(dstType), rewriter);
425 if (!dstAttr)
426 return failure();
427 rewriter.replaceOpWithNewOp<spirv::ConstantOp>(constOp, dstType, dstAttr);
428 return success();
429 }
430};
431
432//===----------------------------------------------------------------------===//
433// RemSIOp
434//===----------------------------------------------------------------------===//
435
436/// Returns signed remainder for `lhs` and `rhs` and lets the result follow
437/// the sign of `signOperand`.
438///
439/// Note that this is needed for Vulkan. Per the Vulkan's SPIR-V environment
440/// spec, "for the OpSRem and OpSMod instructions, if either operand is negative
441/// the result is undefined." So we cannot directly use spirv.SRem/spirv.SMod
442/// if either operand can be negative. Emulate it via spirv.UMod.
443template <typename SignedAbsOp>
444static Value emulateSignedRemainder(Location loc, Value lhs, Value rhs,
445 Value signOperand, OpBuilder &builder) {
446 assert(lhs.getType() == rhs.getType());
447 assert(lhs == signOperand || rhs == signOperand);
448
449 Type type = lhs.getType();
450
451 // Calculate the remainder with spirv.UMod.
452 Value lhsAbs = SignedAbsOp::create(builder, loc, type, lhs);
453 Value rhsAbs = SignedAbsOp::create(builder, loc, type, rhs);
454 Value abs = spirv::UModOp::create(builder, loc, lhsAbs, rhsAbs);
455
456 // Fix the sign.
457 Value isPositive;
458 if (lhs == signOperand)
459 isPositive = spirv::IEqualOp::create(builder, loc, lhs, lhsAbs);
460 else
461 isPositive = spirv::IEqualOp::create(builder, loc, rhs, rhsAbs);
462 Value absNegate = spirv::SNegateOp::create(builder, loc, type, abs);
463 return spirv::SelectOp::create(builder, loc, type, isPositive, abs,
464 absNegate);
465}
466
467/// Converts arith.remsi to GLSL SPIR-V ops.
468///
469/// This cannot be merged into the template unary/binary pattern due to Vulkan
470/// restrictions over spirv.SRem and spirv.SMod.
471struct RemSIOpGLPattern final : public OpConversionPattern<arith::RemSIOp> {
472 using Base::Base;
473
474 LogicalResult
475 matchAndRewrite(arith::RemSIOp op, OpAdaptor adaptor,
476 ConversionPatternRewriter &rewriter) const override {
477 Value result = emulateSignedRemainder<spirv::GLSAbsOp>(
478 op.getLoc(), adaptor.getOperands()[0], adaptor.getOperands()[1],
479 adaptor.getOperands()[0], rewriter);
480 rewriter.replaceOp(op, result);
481
482 return success();
483 }
484};
485
486/// Converts arith.remsi to OpenCL SPIR-V ops.
487struct RemSIOpCLPattern final : public OpConversionPattern<arith::RemSIOp> {
488 using Base::Base;
489
490 LogicalResult
491 matchAndRewrite(arith::RemSIOp op, OpAdaptor adaptor,
492 ConversionPatternRewriter &rewriter) const override {
493 Value result = emulateSignedRemainder<spirv::CLSAbsOp>(
494 op.getLoc(), adaptor.getOperands()[0], adaptor.getOperands()[1],
495 adaptor.getOperands()[0], rewriter);
496 rewriter.replaceOp(op, result);
497
498 return success();
499 }
500};
501
502//===----------------------------------------------------------------------===//
503// BitwiseOp
504//===----------------------------------------------------------------------===//
505
506/// Converts bitwise operations to SPIR-V operations. This is a special pattern
507/// other than the BinaryOpPatternPattern because if the operands are boolean
508/// values, SPIR-V uses different operations (`SPIRVLogicalOp`). For
509/// non-boolean operands, SPIR-V should use `SPIRVBitwiseOp`.
510template <typename Op, typename SPIRVLogicalOp, typename SPIRVBitwiseOp>
511struct BitwiseOpPattern final : public OpConversionPattern<Op> {
512 using OpConversionPattern<Op>::OpConversionPattern;
513
514 LogicalResult
515 matchAndRewrite(Op op, typename Op::Adaptor adaptor,
516 ConversionPatternRewriter &rewriter) const override {
517 assert(adaptor.getOperands().size() == 2);
518 Type dstType = this->getTypeConverter()->convertType(op.getType());
519 if (!dstType)
520 return getTypeConversionFailure(rewriter, op);
521
522 if (isBoolScalarOrVector(adaptor.getOperands().front().getType())) {
523 rewriter.template replaceOpWithNewOp<SPIRVLogicalOp>(
524 op, dstType, adaptor.getOperands());
525 } else {
526 rewriter.template replaceOpWithNewOp<SPIRVBitwiseOp>(
527 op, dstType, adaptor.getOperands());
528 }
529 return success();
530 }
531};
532
533//===----------------------------------------------------------------------===//
534// XOrIOp
535//===----------------------------------------------------------------------===//
536
537/// Converts arith.xori to SPIR-V operations.
538struct XOrIOpLogicalPattern final : public OpConversionPattern<arith::XOrIOp> {
539 using Base::Base;
540
541 LogicalResult
542 matchAndRewrite(arith::XOrIOp op, OpAdaptor adaptor,
543 ConversionPatternRewriter &rewriter) const override {
544 assert(adaptor.getOperands().size() == 2);
545
546 if (isBoolScalarOrVector(adaptor.getOperands().front().getType()))
547 return failure();
548
549 Type dstType = getTypeConverter()->convertType(op.getType());
550 if (!dstType)
551 return getTypeConversionFailure(rewriter, op);
552
553 rewriter.replaceOpWithNewOp<spirv::BitwiseXorOp>(op, dstType,
554 adaptor.getOperands());
555
556 return success();
557 }
558};
559
560/// Converts arith.xori to SPIR-V operations if the type of source is i1 or
561/// vector of i1.
562struct XOrIOpBooleanPattern final : public OpConversionPattern<arith::XOrIOp> {
563 using Base::Base;
564
565 LogicalResult
566 matchAndRewrite(arith::XOrIOp op, OpAdaptor adaptor,
567 ConversionPatternRewriter &rewriter) const override {
568 assert(adaptor.getOperands().size() == 2);
569
570 if (!isBoolScalarOrVector(adaptor.getOperands().front().getType()))
571 return failure();
572
573 Type dstType = getTypeConverter()->convertType(op.getType());
574 if (!dstType)
575 return getTypeConversionFailure(rewriter, op);
576
577 rewriter.replaceOpWithNewOp<spirv::LogicalNotEqualOp>(
578 op, dstType, adaptor.getOperands());
579 return success();
580 }
581};
582
583/// Converts an arith integer op to the given SPIR-V boolean op if the type is
584/// i1 or vector of i1. Each mapping follows from the boolean truth table of
585/// the operation:
586/// addi(a, b) = a ^ b (add mod 2 = XOR = LogicalNotEqual)
587/// subi(a, b) = a ^ b (sub mod 2 = XOR = LogicalNotEqual)
588/// muli(a, b) = a & b (1*1=1, else 0 = LogicalAnd)
589/// divui(a, b) = a & b (a/1=a, a/0=UB; truth table matches AND)
590/// divsi(a, b) = a & b (same as divui on i1)
591/// maxsi(a, b) = a & b (signed i1: 1 represents -1, so max is 0 unless both
592/// are 1)
593/// maxui(a, b) = a | b (unsigned max on i1: 1 when either operand is 1)
594/// minsi(a, b) = a | b (signed i1: -1 < 0, so min is 1 when either operand
595/// is 1)
596/// minui(a, b) = a & b (unsigned min on i1: 1 only when both operands are
597/// 1)
598template <typename ArithOp, typename SPIRVOp>
599struct BoolIOpPattern final : public OpConversionPattern<ArithOp> {
600 BoolIOpPattern(const TypeConverter &converter, MLIRContext *context)
601 // benefit=2: takes priority over the generic ElementwiseArithOpPattern
602 // (benefit=1) when the operand type is i1.
603 : OpConversionPattern<ArithOp>(converter, context, /*benefit=*/2) {}
604
605 LogicalResult
606 matchAndRewrite(ArithOp op, typename ArithOp::Adaptor adaptor,
607 ConversionPatternRewriter &rewriter) const override {
608 if (!isBoolScalarOrVector(adaptor.getOperands().front().getType()))
609 return failure();
610
611 Type dstType = this->getTypeConverter()->convertType(op.getType());
612 if (!dstType)
613 return getTypeConversionFailure(rewriter, op);
614
615 rewriter.replaceOpWithNewOp<SPIRVOp>(op, dstType, adaptor.getOperands());
616 return success();
617 }
618};
619
620/// Converts an arith binary op on i1 to spirv.LogicalAnd(lhs,
621/// spirv.LogicalNot(rhs)). This covers shift-left, shift-right-unsigned, and
622/// unsigned remainder on i1:
623/// shli(a, b) = a & ~b (shift left clears the bit when b=1)
624/// shrui(a, b) = a & ~b (shift right unsigned clears the bit when b=1)
625/// remui(a, b) = a & ~b (only defined when b=1; a%1=0, and ~b=~1=0, so AND
626/// gives 0)
627/// remsi(a, b) = a & ~b (only defined when b=1; a%1=0, and ~b=~1=0, so AND
628/// gives 0)
629template <typename ArithOp>
630struct BoolIOpAndNotPattern final : public OpConversionPattern<ArithOp> {
631 BoolIOpAndNotPattern(const TypeConverter &converter, MLIRContext *context)
632 // benefit=2: takes priority over the generic ElementwiseArithOpPattern
633 // (benefit=1) when the operand type is i1.
634 : OpConversionPattern<ArithOp>(converter, context, /*benefit=*/2) {}
635
636 LogicalResult
637 matchAndRewrite(ArithOp op, typename ArithOp::Adaptor adaptor,
638 ConversionPatternRewriter &rewriter) const override {
639 if (!isBoolScalarOrVector(adaptor.getOperands().front().getType()))
640 return failure();
641
642 Type dstType = this->getTypeConverter()->convertType(op.getType());
643 if (!dstType)
644 return getTypeConversionFailure(rewriter, op);
645
646 Location loc = op.getLoc();
647 Value notRhs = spirv::LogicalNotOp::create(rewriter, loc, dstType,
648 adaptor.getOperands()[1]);
649 rewriter.replaceOpWithNewOp<spirv::LogicalAndOp>(
650 op, dstType, adaptor.getOperands()[0], notRhs);
651 return success();
652 }
653};
654
655/// Converts arith.shrsi on i1 to identity: arithmetic right shift of a 1-bit
656/// signed value always yields the original value (0 >> n = 0, -1 >> n = -1).
657struct ShRSIBoolPattern final : public OpConversionPattern<arith::ShRSIOp> {
658 ShRSIBoolPattern(const TypeConverter &converter, MLIRContext *context)
659 // benefit=2: takes priority over the generic spirv::ElementwiseOpPattern
660 // (benefit=1) when the operand type is i1.
661 : OpConversionPattern<arith::ShRSIOp>(converter, context,
662 /*benefit=*/2) {}
663
664 LogicalResult
665 matchAndRewrite(arith::ShRSIOp op, OpAdaptor adaptor,
666 ConversionPatternRewriter &rewriter) const override {
667 if (!isBoolScalarOrVector(adaptor.getOperands().front().getType()))
668 return failure();
669
670 rewriter.replaceOp(op, adaptor.getOperands().front());
671 return success();
672 }
673};
674
675//===----------------------------------------------------------------------===//
676// i1 source to value
677//===----------------------------------------------------------------------===//
678
679/// Converts an op whose i1 (or vector of i1) source selects between one and
680/// zero of the destination type, i.e. spirv.Select(src, one, zero). Shared by
681/// arith.uitofp, arith.extui, and arith.index_cast on boolean sources.
682template <typename ArithOp>
683struct BoolToValuePattern final : public OpConversionPattern<ArithOp> {
684 using OpConversionPattern<ArithOp>::OpConversionPattern;
685
686 LogicalResult
687 matchAndRewrite(ArithOp op, typename ArithOp::Adaptor adaptor,
688 ConversionPatternRewriter &rewriter) const override {
689 Type srcType = adaptor.getOperands().front().getType();
690 if (!isBoolScalarOrVector(srcType))
691 return failure();
692
693 Type dstType = this->getTypeConverter()->convertType(op.getType());
694 if (!dstType)
695 return getTypeConversionFailure(rewriter, op);
696
697 Location loc = op.getLoc();
698 Value zero = spirv::ConstantOp::getZero(dstType, loc, rewriter);
699 Value one = spirv::ConstantOp::getOne(dstType, loc, rewriter);
700 rewriter.replaceOpWithNewOp<spirv::SelectOp>(
701 op, dstType, adaptor.getOperands().front(), one, zero);
702 return success();
703 }
704};
705
706//===----------------------------------------------------------------------===//
707// UIToFPOp
708//===----------------------------------------------------------------------===//
709
710/// Converts arith.uitofp/arith.sitofp to spirv.ConvertUToF/spirv.ConvertSToF.
711/// When the source integer type was widened during type conversion (e.g., i8
712/// emulated as i32), the upper bits of the widened value may contain garbage.
713/// This pattern cleans the upper bits before the conversion:
714/// - For unsigned (IsSigned=false): mask with BitwiseAnd.
715/// - For signed (IsSigned=true): sign-extend via ShiftLeftLogical +
716/// ShiftRightArithmetic.
717template <typename ArithOp, typename SPIRVOp, bool IsSigned>
718struct IntToFPPattern final : public OpConversionPattern<ArithOp> {
719 using OpConversionPattern<ArithOp>::OpConversionPattern;
720
721 LogicalResult
722 matchAndRewrite(ArithOp op, typename ArithOp::Adaptor adaptor,
723 ConversionPatternRewriter &rewriter) const override {
724 Type srcType = adaptor.getOperands().front().getType();
725 if (isBoolScalarOrVector(srcType))
726 return failure();
727
728 Type dstType = this->getTypeConverter()->convertType(op.getType());
729 if (!dstType)
730 return getTypeConversionFailure(rewriter, op);
731
732 // Check if the source integer type was widened during type conversion.
733 unsigned originalBitwidth =
734 getElementTypeOrSelf(op.getIn().getType()).getIntOrFloatBitWidth();
735
736 Type srcElemType = getElementTypeOrSelf(srcType);
737 if (!srcElemType.isIntOrFloat()) {
738 return rewriter.notifyMatchFailure(op,
739 "unsupported type for uitofp/sitofp");
740 }
741
742 unsigned convertedBitwidth = srcElemType.getIntOrFloatBitWidth();
743
744 if (originalBitwidth >= convertedBitwidth) {
745 rewriter.replaceOpWithNewOp<SPIRVOp>(op, dstType, adaptor.getOperands());
746 return success();
747 }
748
749 // The source was widened. Clean the upper bits before converting.
750 Location loc = op.getLoc();
751 Value cleaned;
752 if constexpr (IsSigned) {
753 // Sign-extend by shifting left then arithmetic right.
754 unsigned shiftAmount = convertedBitwidth - originalBitwidth;
755 Value shiftSize =
756 getScalarOrVectorConstInt(srcType, shiftAmount, rewriter, loc);
757 Value shifted = spirv::ShiftLeftLogicalOp::create(
758 rewriter, loc, srcType, adaptor.getIn(), shiftSize);
759 cleaned = spirv::ShiftRightArithmeticOp::create(rewriter, loc, srcType,
760 shifted, shiftSize);
761 } else {
762 // Zero-extend by masking off the upper bits.
763 Value mask = getScalarOrVectorConstInt(
764 srcType, llvm::maskTrailingOnes<uint64_t>(originalBitwidth), rewriter,
765 loc);
766 cleaned = spirv::BitwiseAndOp::create(rewriter, loc, srcType,
767 adaptor.getIn(), mask);
768 }
769 rewriter.replaceOpWithNewOp<SPIRVOp>(op, dstType, cleaned);
770 return success();
771 }
772};
773
774//===----------------------------------------------------------------------===//
775// IndexCastOp
776//===----------------------------------------------------------------------===//
777
778/// Converts arith.index_cast to spirv.INotEqual if the target type is i1.
779struct IndexCastIndexI1Pattern final
780 : public OpConversionPattern<arith::IndexCastOp> {
781 using Base::Base;
782
783 LogicalResult
784 matchAndRewrite(arith::IndexCastOp op, OpAdaptor adaptor,
785 ConversionPatternRewriter &rewriter) const override {
786 if (!isBoolScalarOrVector(op.getType()))
787 return failure();
788
789 Type dstType = getTypeConverter()->convertType(op.getType());
790 if (!dstType)
791 return getTypeConversionFailure(rewriter, op);
792
793 Location loc = op.getLoc();
794 Value zeroIdx =
795 spirv::ConstantOp::getZero(adaptor.getIn().getType(), loc, rewriter);
796 rewriter.replaceOpWithNewOp<spirv::INotEqualOp>(op, dstType, zeroIdx,
797 adaptor.getIn());
798 return success();
799 }
800};
801
802//===----------------------------------------------------------------------===//
803// ExtSIOp
804//===----------------------------------------------------------------------===//
805
806/// Converts arith.extsi to spirv.Select if the type of source is i1 or vector
807/// of i1.
808struct ExtSII1Pattern final : public OpConversionPattern<arith::ExtSIOp> {
809 using Base::Base;
810
811 LogicalResult
812 matchAndRewrite(arith::ExtSIOp op, OpAdaptor adaptor,
813 ConversionPatternRewriter &rewriter) const override {
814 Value operand = adaptor.getIn();
815 if (!isBoolScalarOrVector(operand.getType()))
816 return failure();
817
818 Location loc = op.getLoc();
819 Type dstType = getTypeConverter()->convertType(op.getType());
820 if (!dstType)
821 return getTypeConversionFailure(rewriter, op);
823 Value allOnes;
824 if (auto intTy = dyn_cast<IntegerType>(dstType)) {
825 unsigned componentBitwidth = intTy.getWidth();
826 allOnes = spirv::ConstantOp::create(
827 rewriter, loc, intTy,
828 rewriter.getIntegerAttr(intTy, APInt::getAllOnes(componentBitwidth)));
829 } else if (auto vectorTy = dyn_cast<VectorType>(dstType)) {
830 unsigned componentBitwidth = vectorTy.getElementTypeBitWidth();
831 allOnes = spirv::ConstantOp::create(
832 rewriter, loc, vectorTy,
833 SplatElementsAttr::get(vectorTy,
834 APInt::getAllOnes(componentBitwidth)));
835 } else {
836 return rewriter.notifyMatchFailure(
837 loc, llvm::formatv("unhandled type: {0}", dstType));
838 }
840 Value zero = spirv::ConstantOp::getZero(dstType, loc, rewriter);
841 rewriter.replaceOpWithNewOp<spirv::SelectOp>(op, dstType, operand, allOnes,
842 zero);
843 return success();
844 }
846
847/// Converts arith.extsi to spirv.Select if the type of source is neither i1 nor
848/// vector of i1.
849struct ExtSIPattern final : public OpConversionPattern<arith::ExtSIOp> {
850 using Base::Base;
851
852 LogicalResult
853 matchAndRewrite(arith::ExtSIOp op, OpAdaptor adaptor,
854 ConversionPatternRewriter &rewriter) const override {
855 Type srcType = adaptor.getIn().getType();
856 if (isBoolScalarOrVector(srcType))
857 return failure();
859 Type dstType = getTypeConverter()->convertType(op.getType());
860 if (!dstType)
861 return getTypeConversionFailure(rewriter, op);
862
863 if (dstType == srcType) {
864 // We can have the same source and destination type due to type emulation.
865 // Perform bit shifting to make sure we have the proper leading set bits.
866
867 unsigned srcBW =
868 getElementTypeOrSelf(op.getIn().getType()).getIntOrFloatBitWidth();
869 unsigned dstBW =
871 assert(srcBW < dstBW);
872 Value shiftSize = getScalarOrVectorConstInt(dstType, dstBW - srcBW,
873 rewriter, op.getLoc());
874 if (!shiftSize)
875 return rewriter.notifyMatchFailure(op, "unsupported type for shift");
877 // First shift left to sequeeze out all leading bits beyond the original
878 // bitwidth. Here we need to use the original source and result type's
879 // bitwidth.
880 auto shiftLOp = spirv::ShiftLeftLogicalOp::create(
881 rewriter, op.getLoc(), dstType, adaptor.getIn(), shiftSize);
882
883 // Then we perform arithmetic right shift to make sure we have the right
884 // sign bits for negative values.
885 rewriter.replaceOpWithNewOp<spirv::ShiftRightArithmeticOp>(
886 op, dstType, shiftLOp, shiftSize);
887 } else {
888 rewriter.replaceOpWithNewOp<spirv::SConvertOp>(op, dstType,
889 adaptor.getOperands());
891
892 return success();
893 }
894};
895
896//===----------------------------------------------------------------------===//
897// ExtUIOp
898//===----------------------------------------------------------------------===//
899
900/// Converts arith.extui for cases where the type of source is neither i1 nor
901/// vector of i1.
902struct ExtUIPattern final : public OpConversionPattern<arith::ExtUIOp> {
903 using Base::Base;
904
905 LogicalResult
906 matchAndRewrite(arith::ExtUIOp op, OpAdaptor adaptor,
907 ConversionPatternRewriter &rewriter) const override {
908 Type srcType = adaptor.getIn().getType();
909 if (isBoolScalarOrVector(srcType))
910 return failure();
911
912 Type dstType = getTypeConverter()->convertType(op.getType());
913 if (!dstType)
914 return getTypeConversionFailure(rewriter, op);
915
916 if (dstType == srcType) {
917 // We can have the same source and destination type due to type emulation.
918 // Perform bit masking to make sure we don't pollute downstream consumers
919 // with unwanted bits. Here we need to use the original source type's
920 // bitwidth.
921 unsigned bitwidth =
922 getElementTypeOrSelf(op.getIn().getType()).getIntOrFloatBitWidth();
923 Value mask = getScalarOrVectorConstInt(
924 dstType, llvm::maskTrailingOnes<uint64_t>(bitwidth), rewriter,
925 op.getLoc());
926 if (!mask)
927 return rewriter.notifyMatchFailure(op, "unsupported type for mask");
928 rewriter.replaceOpWithNewOp<spirv::BitwiseAndOp>(op, dstType,
929 adaptor.getIn(), mask);
930 } else {
931 rewriter.replaceOpWithNewOp<spirv::UConvertOp>(op, dstType,
932 adaptor.getOperands());
933 }
934 return success();
935 }
936};
937
938//===----------------------------------------------------------------------===//
939// TruncIOp
940//===----------------------------------------------------------------------===//
941
942/// Converts arith.trunci to spirv.Select if the type of result is i1 or vector
943/// of i1.
944struct TruncII1Pattern final : public OpConversionPattern<arith::TruncIOp> {
945 using Base::Base;
946
947 LogicalResult
948 matchAndRewrite(arith::TruncIOp op, OpAdaptor adaptor,
949 ConversionPatternRewriter &rewriter) const override {
950 Type dstType = getTypeConverter()->convertType(op.getType());
951 if (!dstType)
952 return getTypeConversionFailure(rewriter, op);
953
954 if (!isBoolScalarOrVector(dstType))
955 return failure();
956
957 Location loc = op.getLoc();
958 auto srcType = adaptor.getOperands().front().getType();
959 // Check if (x & 1) == 1.
960 Value mask = spirv::ConstantOp::getOne(srcType, loc, rewriter);
961 Value maskedSrc = spirv::BitwiseAndOp::create(
962 rewriter, loc, srcType, adaptor.getOperands()[0], mask);
963 Value isOne = spirv::IEqualOp::create(rewriter, loc, maskedSrc, mask);
964
965 Value zero = spirv::ConstantOp::getZero(dstType, loc, rewriter);
966 Value one = spirv::ConstantOp::getOne(dstType, loc, rewriter);
967 rewriter.replaceOpWithNewOp<spirv::SelectOp>(op, dstType, isOne, one, zero);
968 return success();
969 }
970};
971
972/// Converts arith.trunci for cases where the type of result is neither i1
973/// nor vector of i1.
974struct TruncIPattern final : public OpConversionPattern<arith::TruncIOp> {
975 using Base::Base;
976
977 LogicalResult
978 matchAndRewrite(arith::TruncIOp op, OpAdaptor adaptor,
979 ConversionPatternRewriter &rewriter) const override {
980 Type srcType = adaptor.getIn().getType();
981 Type dstType = getTypeConverter()->convertType(op.getType());
982 if (!dstType)
983 return getTypeConversionFailure(rewriter, op);
984
985 if (isBoolScalarOrVector(dstType))
986 return failure();
987
988 if (dstType == srcType) {
989 // We can have the same source and destination type due to type emulation.
990 // Perform bit masking to make sure we don't pollute downstream consumers
991 // with unwanted bits. Here we need to use the original result type's
992 // bitwidth.
993 unsigned bw = getElementTypeOrSelf(op.getType()).getIntOrFloatBitWidth();
994 Value mask = getScalarOrVectorConstInt(
995 dstType, llvm::maskTrailingOnes<uint64_t>(bw), rewriter, op.getLoc());
996 if (!mask)
997 return rewriter.notifyMatchFailure(op, "unsupported type for mask");
998 rewriter.replaceOpWithNewOp<spirv::BitwiseAndOp>(op, dstType,
999 adaptor.getIn(), mask);
1000 } else {
1001 // Given this is truncation, either SConvertOp or UConvertOp works.
1002 rewriter.replaceOpWithNewOp<spirv::SConvertOp>(op, dstType,
1003 adaptor.getOperands());
1004 }
1005 return success();
1006 }
1007};
1008
1009//===----------------------------------------------------------------------===//
1010// TypeCastingOp
1011//===----------------------------------------------------------------------===//
1012
1013static std::optional<spirv::FPRoundingMode>
1014convertArithRoundingModeToSPIRV(arith::RoundingMode roundingMode) {
1015 switch (roundingMode) {
1016 case arith::RoundingMode::downward:
1017 return spirv::FPRoundingMode::RTN;
1018 case arith::RoundingMode::to_nearest_even:
1019 return spirv::FPRoundingMode::RTE;
1020 case arith::RoundingMode::toward_zero:
1021 return spirv::FPRoundingMode::RTZ;
1022 case arith::RoundingMode::upward:
1023 return spirv::FPRoundingMode::RTP;
1024 case arith::RoundingMode::to_nearest_away:
1025 // SPIR-V FPRoundingMode decoration has no ties-away-from-zero mode
1026 // (as of SPIR-V 1.6)
1027 return std::nullopt;
1028 }
1029 llvm_unreachable("Unhandled rounding mode");
1030}
1031
1032/// Converts type-casting standard operations to SPIR-V operations.
1033template <typename Op, typename SPIRVOp>
1034struct TypeCastingOpPattern final : public OpConversionPattern<Op> {
1035 using OpConversionPattern<Op>::OpConversionPattern;
1036
1037 LogicalResult
1038 matchAndRewrite(Op op, typename Op::Adaptor adaptor,
1039 ConversionPatternRewriter &rewriter) const override {
1040 Type srcType = llvm::getSingleElement(adaptor.getOperands()).getType();
1041 Type dstType = this->getTypeConverter()->convertType(op.getType());
1042 if (!dstType)
1043 return getTypeConversionFailure(rewriter, op);
1044
1045 if (isBoolScalarOrVector(srcType) || isBoolScalarOrVector(dstType))
1046 return failure();
1047
1048 if (dstType == srcType) {
1049 // Due to type conversion, we are seeing the same source and target type.
1050 // Then we can just erase this operation by forwarding its operand.
1051 rewriter.replaceOp(op, adaptor.getOperands().front());
1052 } else {
1053 // Compute new rounding mode (if any).
1054 std::optional<spirv::FPRoundingMode> rm = std::nullopt;
1055 if (auto roundingModeOp =
1056 dyn_cast<arith::ArithRoundingModeInterface>(*op)) {
1057 if (arith::RoundingModeAttr roundingMode =
1058 roundingModeOp.getRoundingModeAttr()) {
1059 if (!(rm =
1060 convertArithRoundingModeToSPIRV(roundingMode.getValue()))) {
1061 return rewriter.notifyMatchFailure(
1062 op->getLoc(),
1063 llvm::formatv("unsupported rounding mode '{0}'", roundingMode));
1064 }
1065 }
1066 }
1067 // Create replacement op and attach rounding mode attribute (if any).
1068 auto newOp = rewriter.template replaceOpWithNewOp<SPIRVOp>(
1069 op, dstType, adaptor.getOperands());
1070 if (rm) {
1071 newOp->setDiscardableAttr(
1072 getDecorationString(spirv::Decoration::FPRoundingMode),
1073 spirv::FPRoundingModeAttr::get(rewriter.getContext(), *rm));
1074 }
1075 }
1076 return success();
1077 }
1078};
1079
1080//===----------------------------------------------------------------------===//
1081// CmpIOp
1082//===----------------------------------------------------------------------===//
1083
1084/// Converts integer compare operation on i1 type operands to SPIR-V ops.
1085class CmpIOpBooleanPattern final : public OpConversionPattern<arith::CmpIOp> {
1086public:
1087 using Base::Base;
1088
1089 LogicalResult
1090 matchAndRewrite(arith::CmpIOp op, OpAdaptor adaptor,
1091 ConversionPatternRewriter &rewriter) const override {
1092 Type srcType = op.getLhs().getType();
1093 if (!isBoolScalarOrVector(srcType))
1094 return failure();
1095 Type dstType = getTypeConverter()->convertType(srcType);
1096 if (!dstType)
1097 return getTypeConversionFailure(rewriter, op, srcType);
1098
1099 switch (op.getPredicate()) {
1100 case arith::CmpIPredicate::eq: {
1101 rewriter.replaceOpWithNewOp<spirv::LogicalEqualOp>(op, adaptor.getLhs(),
1102 adaptor.getRhs());
1103 return success();
1104 }
1105 case arith::CmpIPredicate::ne: {
1106 rewriter.replaceOpWithNewOp<spirv::LogicalNotEqualOp>(
1107 op, adaptor.getLhs(), adaptor.getRhs());
1108 return success();
1109 }
1110 case arith::CmpIPredicate::uge:
1111 case arith::CmpIPredicate::ugt:
1112 case arith::CmpIPredicate::ule:
1113 case arith::CmpIPredicate::ult: {
1114 // There are no direct corresponding instructions in SPIR-V for such
1115 // cases. Extend them to 32-bit and do comparision then.
1116 Type type = rewriter.getI32Type();
1117 if (auto vectorType = dyn_cast<VectorType>(dstType))
1118 type = VectorType::get(vectorType.getShape(), type);
1119 Value extLhs =
1120 arith::ExtUIOp::create(rewriter, op.getLoc(), type, adaptor.getLhs());
1121 Value extRhs =
1122 arith::ExtUIOp::create(rewriter, op.getLoc(), type, adaptor.getRhs());
1123
1124 rewriter.replaceOpWithNewOp<arith::CmpIOp>(op, op.getPredicate(), extLhs,
1125 extRhs);
1126 return success();
1127 }
1128 default:
1129 break;
1130 }
1131 return failure();
1132 }
1133};
1134
1135/// Converts integer compare operation to SPIR-V ops.
1136class CmpIOpPattern final : public OpConversionPattern<arith::CmpIOp> {
1137public:
1138 using Base::Base;
1139
1140 LogicalResult
1141 matchAndRewrite(arith::CmpIOp op, OpAdaptor adaptor,
1142 ConversionPatternRewriter &rewriter) const override {
1143 Type srcType = op.getLhs().getType();
1144 if (isBoolScalarOrVector(srcType))
1145 return failure();
1146 Type dstType = getTypeConverter()->convertType(srcType);
1147 if (!dstType)
1148 return getTypeConversionFailure(rewriter, op, srcType);
1149
1150 switch (op.getPredicate()) {
1151#define DISPATCH(cmpPredicate, spirvOp) \
1152 case cmpPredicate: \
1153 if (spirvOp::template hasTrait<OpTrait::spirv::UnsignedOp>() && \
1154 !getElementTypeOrSelf(srcType).isIndex() && srcType != dstType && \
1155 !hasSameBitwidth(srcType, dstType)) { \
1156 return op.emitError( \
1157 "bitwidth emulation is not implemented yet on unsigned op"); \
1158 } \
1159 rewriter.replaceOpWithNewOp<spirvOp>(op, adaptor.getLhs(), \
1160 adaptor.getRhs()); \
1161 return success();
1162
1163 DISPATCH(arith::CmpIPredicate::eq, spirv::IEqualOp);
1164 DISPATCH(arith::CmpIPredicate::ne, spirv::INotEqualOp);
1165 DISPATCH(arith::CmpIPredicate::slt, spirv::SLessThanOp);
1166 DISPATCH(arith::CmpIPredicate::sle, spirv::SLessThanEqualOp);
1167 DISPATCH(arith::CmpIPredicate::sgt, spirv::SGreaterThanOp);
1168 DISPATCH(arith::CmpIPredicate::sge, spirv::SGreaterThanEqualOp);
1169 DISPATCH(arith::CmpIPredicate::ult, spirv::ULessThanOp);
1170 DISPATCH(arith::CmpIPredicate::ule, spirv::ULessThanEqualOp);
1171 DISPATCH(arith::CmpIPredicate::ugt, spirv::UGreaterThanOp);
1172 DISPATCH(arith::CmpIPredicate::uge, spirv::UGreaterThanEqualOp);
1173
1174#undef DISPATCH
1175 }
1176 return failure();
1177 }
1178};
1179
1180//===----------------------------------------------------------------------===//
1181// CmpFOpPattern
1182//===----------------------------------------------------------------------===//
1183
1184/// Converts floating-point comparison operations to SPIR-V ops.
1185class CmpFOpPattern final : public OpConversionPattern<arith::CmpFOp> {
1186public:
1187 using Base::Base;
1188
1189 LogicalResult
1190 matchAndRewrite(arith::CmpFOp op, OpAdaptor adaptor,
1191 ConversionPatternRewriter &rewriter) const override {
1192 switch (op.getPredicate()) {
1193#define DISPATCH(cmpPredicate, spirvOp) \
1194 case cmpPredicate: \
1195 rewriter.replaceOpWithNewOp<spirvOp>(op, adaptor.getLhs(), \
1196 adaptor.getRhs()); \
1197 return success();
1198
1199 // Ordered.
1200 DISPATCH(arith::CmpFPredicate::OEQ, spirv::FOrdEqualOp);
1201 DISPATCH(arith::CmpFPredicate::OGT, spirv::FOrdGreaterThanOp);
1202 DISPATCH(arith::CmpFPredicate::OGE, spirv::FOrdGreaterThanEqualOp);
1203 DISPATCH(arith::CmpFPredicate::OLT, spirv::FOrdLessThanOp);
1204 DISPATCH(arith::CmpFPredicate::OLE, spirv::FOrdLessThanEqualOp);
1205 DISPATCH(arith::CmpFPredicate::ONE, spirv::FOrdNotEqualOp);
1206 // Unordered.
1207 DISPATCH(arith::CmpFPredicate::UEQ, spirv::FUnordEqualOp);
1208 DISPATCH(arith::CmpFPredicate::UGT, spirv::FUnordGreaterThanOp);
1209 DISPATCH(arith::CmpFPredicate::UGE, spirv::FUnordGreaterThanEqualOp);
1210 DISPATCH(arith::CmpFPredicate::ULT, spirv::FUnordLessThanOp);
1211 DISPATCH(arith::CmpFPredicate::ULE, spirv::FUnordLessThanEqualOp);
1212 DISPATCH(arith::CmpFPredicate::UNE, spirv::FUnordNotEqualOp);
1213
1214#undef DISPATCH
1215
1216 default:
1217 break;
1218 }
1219 return failure();
1220 }
1221};
1222
1223/// Converts floating point NaN check to SPIR-V ops. This pattern requires
1224/// Kernel capability.
1225class CmpFOpNanKernelPattern final : public OpConversionPattern<arith::CmpFOp> {
1226public:
1227 using Base::Base;
1228
1229 LogicalResult
1230 matchAndRewrite(arith::CmpFOp op, OpAdaptor adaptor,
1231 ConversionPatternRewriter &rewriter) const override {
1232 if (op.getPredicate() == arith::CmpFPredicate::ORD) {
1233 rewriter.replaceOpWithNewOp<spirv::OrderedOp>(op, adaptor.getLhs(),
1234 adaptor.getRhs());
1235 return success();
1236 }
1237
1238 if (op.getPredicate() == arith::CmpFPredicate::UNO) {
1239 rewriter.replaceOpWithNewOp<spirv::UnorderedOp>(op, adaptor.getLhs(),
1240 adaptor.getRhs());
1241 return success();
1242 }
1243
1244 return failure();
1245 }
1246};
1247
1248/// Converts floating point NaN check to SPIR-V ops. This pattern does not
1249/// require additional capability.
1250class CmpFOpNanNonePattern final : public OpConversionPattern<arith::CmpFOp> {
1251public:
1252 using Base::Base;
1253
1254 LogicalResult
1255 matchAndRewrite(arith::CmpFOp op, OpAdaptor adaptor,
1256 ConversionPatternRewriter &rewriter) const override {
1257 if (op.getPredicate() != arith::CmpFPredicate::ORD &&
1258 op.getPredicate() != arith::CmpFPredicate::UNO)
1259 return failure();
1260
1261 Location loc = op.getLoc();
1262
1263 Value replace;
1264 if (bitEnumContainsAll(op.getFastmath(), arith::FastMathFlags::nnan)) {
1265 if (op.getPredicate() == arith::CmpFPredicate::ORD) {
1266 // Ordered comparsion checks if neither operand is NaN.
1267 replace = spirv::ConstantOp::getOne(op.getType(), loc, rewriter);
1268 } else {
1269 // Unordered comparsion checks if either operand is NaN.
1270 replace = spirv::ConstantOp::getZero(op.getType(), loc, rewriter);
1271 }
1272 } else {
1273 Value lhsIsNan = spirv::IsNanOp::create(rewriter, loc, adaptor.getLhs());
1274 Value rhsIsNan = spirv::IsNanOp::create(rewriter, loc, adaptor.getRhs());
1275
1276 replace = spirv::LogicalOrOp::create(rewriter, loc, lhsIsNan, rhsIsNan);
1277 if (op.getPredicate() == arith::CmpFPredicate::ORD)
1278 replace = spirv::LogicalNotOp::create(rewriter, loc, replace);
1279 }
1280
1281 rewriter.replaceOp(op, replace);
1282 return success();
1283 }
1284};
1285
1286//===----------------------------------------------------------------------===//
1287// AddUIExtendedOp/SubUIExtendedOp
1288//===----------------------------------------------------------------------===//
1289
1290/// Converts arith.addui_extended/arith.subui_extended to spirv.IAddCarry/
1291/// spirv.ISubBorrow.
1292template <typename ArithExtendedOp, typename SPIRVExtendedOp>
1293class BinaryExtendedOpPattern final
1294 : public OpConversionPattern<ArithExtendedOp> {
1295public:
1296 using OpConversionPattern<ArithExtendedOp>::OpConversionPattern;
1297 LogicalResult
1298 matchAndRewrite(ArithExtendedOp op, typename ArithExtendedOp::Adaptor adaptor,
1299 ConversionPatternRewriter &rewriter) const override {
1300 Type dstElemTy = adaptor.getLhs().getType();
1301 Location loc = op->getLoc();
1302 Value result = SPIRVExtendedOp::create(rewriter, loc, adaptor.getLhs(),
1303 adaptor.getRhs());
1304
1305 Value valueResult = spirv::CompositeExtractOp::create(rewriter, loc, result,
1306 llvm::ArrayRef(0));
1307 Value flagValue = spirv::CompositeExtractOp::create(rewriter, loc, result,
1308 llvm::ArrayRef(1));
1309
1310 // Convert the carry/borrow value to boolean.
1311 Value one = spirv::ConstantOp::getOne(dstElemTy, loc, rewriter);
1312 Value flagResult = spirv::IEqualOp::create(rewriter, loc, flagValue, one);
1313
1314 rewriter.replaceOp(op, {valueResult, flagResult});
1315 return success();
1316 }
1317};
1318
1319//===----------------------------------------------------------------------===//
1320// MulIExtendedOp
1321//===----------------------------------------------------------------------===//
1322
1323/// Converts arith.mul*i_extended to spirv.*MulExtended.
1324template <typename ArithMulOp, typename SPIRVMulOp>
1325class MulIExtendedOpPattern final : public OpConversionPattern<ArithMulOp> {
1326public:
1327 using OpConversionPattern<ArithMulOp>::OpConversionPattern;
1328 LogicalResult
1329 matchAndRewrite(ArithMulOp op, typename ArithMulOp::Adaptor adaptor,
1330 ConversionPatternRewriter &rewriter) const override {
1331 Location loc = op->getLoc();
1332 Value result =
1333 SPIRVMulOp::create(rewriter, loc, adaptor.getLhs(), adaptor.getRhs());
1334
1335 Value low = spirv::CompositeExtractOp::create(rewriter, loc, result,
1336 llvm::ArrayRef(0));
1337 Value high = spirv::CompositeExtractOp::create(rewriter, loc, result,
1338 llvm::ArrayRef(1));
1339
1340 rewriter.replaceOp(op, {low, high});
1341 return success();
1342 }
1343};
1344
1345//===----------------------------------------------------------------------===//
1346// SelectOp
1347//===----------------------------------------------------------------------===//
1348
1349/// Converts arith.select to spirv.Select.
1350class SelectOpPattern final : public OpConversionPattern<arith::SelectOp> {
1351public:
1352 using Base::Base;
1353 LogicalResult
1354 matchAndRewrite(arith::SelectOp op, OpAdaptor adaptor,
1355 ConversionPatternRewriter &rewriter) const override {
1356 rewriter.replaceOpWithNewOp<spirv::SelectOp>(op, adaptor.getCondition(),
1357 adaptor.getTrueValue(),
1358 adaptor.getFalseValue());
1359 return success();
1360 }
1361};
1362
1363//===----------------------------------------------------------------------===//
1364// MinimumFOp, MaximumFOp
1365//===----------------------------------------------------------------------===//
1366
1367/// Converts arith.maximumf/minimumf to spirv.GL.FMax/FMin or
1368/// spirv.CL.fmax/fmin.
1369template <typename Op, typename SPIRVOp>
1370class MinimumMaximumFOpPattern final : public OpConversionPattern<Op> {
1371public:
1372 using OpConversionPattern<Op>::OpConversionPattern;
1373 LogicalResult
1374 matchAndRewrite(Op op, typename Op::Adaptor adaptor,
1375 ConversionPatternRewriter &rewriter) const override {
1376 auto *converter = this->template getTypeConverter<SPIRVTypeConverter>();
1377 Type dstType = converter->convertType(op.getType());
1378 if (!dstType)
1379 return getTypeConversionFailure(rewriter, op);
1380
1381 // arith.maximumf/minimumf:
1382 // "if one of the arguments is NaN, then the result is also NaN."
1383 // spirv.GL.FMax/FMin
1384 // "which operand is the result is undefined if one of the operands
1385 // is a NaN."
1386 // spirv.CL.fmax/fmin:
1387 // "If one argument is a NaN, Fmin returns the other argument."
1388
1389 Location loc = op.getLoc();
1390 Value spirvOp =
1391 SPIRVOp::create(rewriter, loc, dstType, adaptor.getOperands());
1392
1393 if (bitEnumContainsAll(op.getFastmath(), arith::FastMathFlags::nnan)) {
1394 rewriter.replaceOp(op, spirvOp);
1395 return success();
1396 }
1397
1398 Value lhsIsNan = spirv::IsNanOp::create(rewriter, loc, adaptor.getLhs());
1399 Value rhsIsNan = spirv::IsNanOp::create(rewriter, loc, adaptor.getRhs());
1400
1401 Value select1 = spirv::SelectOp::create(rewriter, loc, dstType, lhsIsNan,
1402 adaptor.getLhs(), spirvOp);
1403 Value select2 = spirv::SelectOp::create(rewriter, loc, dstType, rhsIsNan,
1404 adaptor.getRhs(), select1);
1405
1406 rewriter.replaceOp(op, select2);
1407 return success();
1408 }
1409};
1410
1411//===----------------------------------------------------------------------===//
1412// MinNumFOp, MaxNumFOp
1413//===----------------------------------------------------------------------===//
1414
1415/// Converts arith.maxnumf/minnumf to spirv.GL.NMax/NMin or
1416/// spirv.CL.fmax/fmin.
1417template <typename Op, typename SPIRVOp>
1418class MinNumMaxNumFOpPattern final : public OpConversionPattern<Op> {
1419 template <typename TargetOp>
1420 constexpr bool shouldInsertNanGuards() const {
1421 return llvm::is_one_of<TargetOp, spirv::GLFMaxOp, spirv::GLFMinOp>::value;
1422 }
1423
1424public:
1425 using OpConversionPattern<Op>::OpConversionPattern;
1426 LogicalResult
1427 matchAndRewrite(Op op, typename Op::Adaptor adaptor,
1428 ConversionPatternRewriter &rewriter) const override {
1429 auto *converter = this->template getTypeConverter<SPIRVTypeConverter>();
1430 Type dstType = converter->convertType(op.getType());
1431 if (!dstType)
1432 return getTypeConversionFailure(rewriter, op);
1433
1434 // arith.maxnumf/minnumf:
1435 // "If one of the arguments is NaN, then the result is the other
1436 // argument."
1437 // spirv.GL.NMax/NMin: NaN is treated as missing, matches arith semantics.
1438 // spirv.CL.fmax/fmin:
1439 // "If one argument is a NaN, Fmin returns the other argument."
1440 // spirv.GL.FMax/FMin: undefined when either operand is NaN, requires
1441 // select guards to implement arith.maxnumf semantics.
1442
1443 Location loc = op.getLoc();
1444 Value spirvOp =
1445 SPIRVOp::create(rewriter, loc, dstType, adaptor.getOperands());
1446
1447 if (!shouldInsertNanGuards<SPIRVOp>() ||
1448 bitEnumContainsAll(op.getFastmath(), arith::FastMathFlags::nnan)) {
1449 rewriter.replaceOp(op, spirvOp);
1450 return success();
1451 }
1452
1453 Value lhsIsNan = spirv::IsNanOp::create(rewriter, loc, adaptor.getLhs());
1454 Value rhsIsNan = spirv::IsNanOp::create(rewriter, loc, adaptor.getRhs());
1455
1456 Value select1 = spirv::SelectOp::create(rewriter, loc, dstType, lhsIsNan,
1457 adaptor.getRhs(), spirvOp);
1458 Value select2 = spirv::SelectOp::create(rewriter, loc, dstType, rhsIsNan,
1459 adaptor.getLhs(), select1);
1460
1461 rewriter.replaceOp(op, select2);
1462 return success();
1463 }
1464};
1465
1466} // namespace
1467
1468//===----------------------------------------------------------------------===//
1469// Pattern Population
1470//===----------------------------------------------------------------------===//
1471
1473 const SPIRVTypeConverter &typeConverter, RewritePatternSet &patterns) {
1474 // clang-format off
1475 patterns.add<
1476 ConstantCompositeOpPattern,
1477 ConstantScalarOpPattern,
1478 BoolIOpPattern<arith::AddIOp, spirv::LogicalNotEqualOp>, // add mod 2 = XOR = not-equal
1479 ElementwiseArithOpPattern<arith::AddIOp, spirv::IAddOp>,
1480 BoolIOpPattern<arith::SubIOp, spirv::LogicalNotEqualOp>, // sub mod 2 = XOR = not-equal
1481 ElementwiseArithOpPattern<arith::SubIOp, spirv::ISubOp>,
1482 BoolIOpPattern<arith::MulIOp, spirv::LogicalAndOp>, // 1*1=1, else 0 = AND
1483 ElementwiseArithOpPattern<arith::MulIOp, spirv::IMulOp>,
1484 BoolIOpPattern<arith::DivUIOp, spirv::LogicalAndOp>, // a/1=a, a/0=UB; truth table = AND
1486 BoolIOpPattern<arith::DivSIOp, spirv::LogicalAndOp>, // same as divui on i1
1488 BoolIOpAndNotPattern<arith::RemUIOp>, // remui(a,b) = a & ~b (see pattern comment)
1490 BoolIOpAndNotPattern<arith::RemSIOp>, // remsi(a,b) = a & ~b (see pattern comment)
1491 RemSIOpGLPattern, RemSIOpCLPattern,
1492 BitwiseOpPattern<arith::AndIOp, spirv::LogicalAndOp, spirv::BitwiseAndOp>,
1493 BitwiseOpPattern<arith::OrIOp, spirv::LogicalOrOp, spirv::BitwiseOrOp>,
1494 XOrIOpLogicalPattern, XOrIOpBooleanPattern,
1495 BoolIOpAndNotPattern<arith::ShLIOp>, // shli(a,b) = a & ~b (see pattern comment)
1496 ElementwiseArithOpPattern<arith::ShLIOp, spirv::ShiftLeftLogicalOp>,
1497 BoolIOpAndNotPattern<arith::ShRUIOp>, // shrui(a,b) = a & ~b (see pattern comment)
1499 ShRSIBoolPattern, // shrsi(a,b) = a (identity; see pattern comment)
1507 ExtUIPattern, BoolToValuePattern<arith::ExtUIOp>,
1508 ExtSIPattern, ExtSII1Pattern,
1509 TypeCastingOpPattern<arith::ExtFOp, spirv::FConvertOp>,
1510 TruncIPattern, TruncII1Pattern,
1511 TypeCastingOpPattern<arith::TruncFOp, spirv::FConvertOp>,
1512 TypeCastingOpPattern<arith::ConvertFOp, spirv::FConvertOp>,
1513 IntToFPPattern<arith::UIToFPOp, spirv::ConvertUToFOp, false>,
1514 BoolToValuePattern<arith::UIToFPOp>,
1515 IntToFPPattern<arith::SIToFPOp, spirv::ConvertSToFOp, true>,
1516 TypeCastingOpPattern<arith::FPToUIOp, spirv::ConvertFToUOp>,
1517 TypeCastingOpPattern<arith::FPToSIOp, spirv::ConvertFToSOp>,
1518 TypeCastingOpPattern<arith::IndexCastOp, spirv::SConvertOp>,
1519 IndexCastIndexI1Pattern, BoolToValuePattern<arith::IndexCastOp>,
1520 TypeCastingOpPattern<arith::IndexCastUIOp, spirv::UConvertOp>,
1521 TypeCastingOpPattern<arith::BitcastOp, spirv::BitcastOp>,
1522 CmpIOpBooleanPattern, CmpIOpPattern,
1523 CmpFOpNanNonePattern, CmpFOpPattern,
1524 BinaryExtendedOpPattern<arith::AddUIExtendedOp, spirv::IAddCarryOp>,
1525 BinaryExtendedOpPattern<arith::SubUIExtendedOp, spirv::ISubBorrowOp>,
1526 MulIExtendedOpPattern<arith::MulSIExtendedOp, spirv::SMulExtendedOp>,
1527 MulIExtendedOpPattern<arith::MulUIExtendedOp, spirv::UMulExtendedOp>,
1528 SelectOpPattern,
1529
1530 MinimumMaximumFOpPattern<arith::MaximumFOp, spirv::GLFMaxOp>,
1531 MinimumMaximumFOpPattern<arith::MinimumFOp, spirv::GLFMinOp>,
1532 MinNumMaxNumFOpPattern<arith::MaxNumFOp, spirv::GLNMaxOp>,
1533 MinNumMaxNumFOpPattern<arith::MinNumFOp, spirv::GLNMinOp>,
1534 BoolIOpPattern<arith::MaxSIOp, spirv::LogicalAndOp>, // signed i1: 1=-1, so max=0 unless both are 1
1535 BoolIOpPattern<arith::MaxUIOp, spirv::LogicalOrOp>, // unsigned max on i1: 1 when either is 1
1536 BoolIOpPattern<arith::MinSIOp, spirv::LogicalOrOp>, // signed i1: -1<0, so min=1 when either is 1
1537 BoolIOpPattern<arith::MinUIOp, spirv::LogicalAndOp>, // unsigned min on i1: 1 only when both are 1
1542
1543 MinimumMaximumFOpPattern<arith::MaximumFOp, spirv::CLFMaxOp>,
1544 MinimumMaximumFOpPattern<arith::MinimumFOp, spirv::CLFMinOp>,
1545 MinNumMaxNumFOpPattern<arith::MaxNumFOp, spirv::CLFMaxOp>,
1546 MinNumMaxNumFOpPattern<arith::MinNumFOp, spirv::CLFMinOp>,
1551 >(typeConverter, patterns.getContext());
1552 // clang-format on
1553
1554 // Give CmpFOpNanKernelPattern a higher benefit so it can prevail when Kernel
1555 // capability is available.
1556 patterns.add<CmpFOpNanKernelPattern>(typeConverter, patterns.getContext(),
1557 /*benefit=*/2);
1558}
1559
1560//===----------------------------------------------------------------------===//
1561// Pass Definition
1562//===----------------------------------------------------------------------===//
1563
1564namespace {
1565struct ConvertArithToSPIRVPass
1566 : public impl::ConvertArithToSPIRVPassBase<ConvertArithToSPIRVPass> {
1567 using Base::Base;
1568
1569 void runOnOperation() override {
1570 Operation *op = getOperation();
1572 std::unique_ptr<SPIRVConversionTarget> target =
1573 SPIRVConversionTarget::get(targetAttr);
1574
1576 options.emulateLT32BitScalarTypes = this->emulateLT32BitScalarTypes;
1577 options.emulateUnsupportedFloatTypes = this->emulateUnsupportedFloatTypes;
1578 SPIRVTypeConverter typeConverter(targetAttr, options);
1579
1580 // Use UnrealizedConversionCast as the bridge so that we don't need to pull
1581 // in patterns for other dialects.
1582 target->addLegalOp<UnrealizedConversionCastOp>();
1583
1584 // Fail hard when there are any remaining 'arith' ops.
1585 target->addIllegalDialect<arith::ArithDialect>();
1586
1587 RewritePatternSet patterns(&getContext());
1588 arith::populateArithToSPIRVPatterns(typeConverter, patterns);
1589
1590 if (failed(applyPartialConversion(op, *target, std::move(patterns))))
1591 signalPassFailure();
1592 }
1593};
1594} // namespace
return success()
static bool hasSameBitwidth(Type a, Type b)
Returns true if scalar/vector type a and b have the same number of bitwidth.
static Value getScalarOrVectorConstInt(Type type, uint64_t value, OpBuilder &builder, Location loc)
Creates a scalar/vector integer constant.
static LogicalResult getTypeConversionFailure(ConversionPatternRewriter &rewriter, Operation *op, Type srcType)
Returns a source type conversion failure for srcType and operation op.
static IntegerAttr getIntegerAttrFromFloatAttr(FloatAttr floatAttr, IntegerType dstType, Builder builder)
static FloatAttr convertFloatAttr(FloatAttr srcAttr, FloatType dstType, Builder builder)
Converts the given srcAttr to a new attribute of the given dstType.
static BoolAttr convertBoolAttr(Attribute srcAttr, Builder builder)
Converts the given srcAttr into a boolean attribute if it holds an integral value.
static bool isBoolScalarOrVector(Type type)
Returns true if the given type is a boolean scalar or vector type.
#define DISPATCH(cmpPredicate, spirvOp)
static IntegerAttr convertIntegerAttr(IntegerAttr srcAttr, IntegerType dstType, Builder builder)
Converts the given srcAttr to a new attribute of the given dstType.
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
b getContext())
static llvm::ManagedStatic< PassManagerOptions > options
This class represents a processed binary blob of data.
Definition AsmState.h:91
ArrayRef< char > getData() const
Return the raw underlying data of this blob.
Definition AsmState.h:145
Attributes are known-constant values of operations.
Definition Attributes.h:25
Special case of IntegerAttr to represent boolean integers, i.e., signless i1 integers.
This class is a general helper class for creating context-global objects like types,...
Definition Builders.h:51
IntegerAttr getIntegerAttr(Type type, int64_t value)
Definition Builders.cpp:237
BoolAttr getBoolAttr(bool value)
Definition Builders.cpp:108
FloatAttr getF32FloatAttr(float value)
Definition Builders.cpp:255
An attribute that represents a reference to a dense vector or tensor object.
auto getValues() const
Return the held element values as a range of the given type.
static DenseElementsAttr getFromRawBuffer(ShapedType type, ArrayRef< char > rawBuffer)
Construct a dense elements attribute from a raw buffer representing the data for this attribute.
static bool isValidRawBuffer(ShapedType type, ArrayRef< char > rawBuffer)
Returns true if the given buffer is a valid raw buffer for the given type.
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
ShapedType getType() const
Return the type of this ElementsAttr, guaranteed to be a vector or tensor with static shape.
DenseElementsAttr reshape(ShapedType newType)
Return a new DenseElementsAttr that has the same data as the current attribute, but has been reshaped...
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
This class helps build Operations.
Definition Builders.h:210
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Location getLoc()
The source location the operation was defined or derived from.
This provides public APIs that all operations should have.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
result_type_range getResultTypes()
Definition Operation.h:453
unsigned getNumResults()
Return the number of results held by this operation.
Definition Operation.h:429
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
static std::unique_ptr< SPIRVConversionTarget > get(spirv::TargetEnvAttr targetAttr)
Creates a SPIR-V conversion target for the given target environment.
Type conversion from builtin types to SPIR-V types for shader interface.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isIndex() const
Definition Types.cpp:56
bool isIntOrIndexOrFloat() const
Return true if this is an integer (of any signedness), index, or float type.
Definition Types.cpp:122
bool isInteger() const
Return true if this is an integer type (with the specified width).
Definition Types.cpp:58
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
Type front()
Return first type in the range.
Definition TypeRange.h:164
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
An attribute that specifies the target version, allowed extensions and capabilities,...
NestedPattern Op(FilterFunctionType filter=defaultFilterFunction)
void populateArithToSPIRVPatterns(const SPIRVTypeConverter &typeConverter, RewritePatternSet &patterns)
TargetEnvAttr lookupTargetEnvOrDefault(Operation *op)
Queries the target environment recursively from enclosing symbol table ops containing the given op or...
std::string getDecorationString(Decoration decoration)
Converts a SPIR-V Decoration enum value to its snake_case string representation for use in MLIR attri...
Include the generated interface declarations.
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
Converts elementwise unary, binary and ternary standard operations to SPIR-V operations.
Definition Pattern.h:24