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